home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / MPW Interfaces & Libraries / AStructMacs / FlowCtlMacs.a next >
Encoding:
Text File  |  1992-01-29  |  138.6 KB  |  2,849 lines  |  [TEXT/MPS ]

  1.         PRINT    Push,NoObj
  2.         TITLE    'FlowCtlMacs - Flow Control Macro Statements'
  3.         
  4. *******************************************************************************
  5. *                                                                             *
  6. *                                 FlowCtlMacs                                 *
  7. *                                                                             *
  8. *                       Flow Control Macro Statements                         *
  9. *                                                                             *
  10. *                                Ira L. Ruben                                 *
  11. *                                  09/15/86                                   *
  12. *                                                                             *
  13. *                  Copyright Apple Computer, Inc. 1986-1989                   *
  14. *                             All rights reserved.                            *
  15. *                                                                             *
  16. *       ---------------------------------------------------------------       *
  17. *                                                                             *
  18. * External macros in this set are:                                            *
  19. *                                                                             *
  20. *   • If#, ElseIf#, Else#, EndIf#     - multi-way decision                    *
  21. *   • Repeat#, Until#                 - loop control                          *
  22. *   • While#, EndW#                   - loop control                          *
  23. *   • For#, EndF#                     - loop control                          *
  24. *   • Switch#, Case#, Default#, EndS# - multi-way decision                    *
  25. *   • Cycle#                          - loop interator                        *
  26. *   • Leave#                          - loop and switch terminator            *
  27. *   • GoTo#                           - transfer of control                   *
  28. *                                                                             *
  29. * Internal macros in this set are:                                            *
  30. *                                                                             *
  31. *   • SExpr# - simple expression parsing                                      *
  32. *   • Expr#  - "full" expression parsing                                      *
  33. *   • Test#  - code generation for a simple expression                        *
  34. *                                                                             *
  35. *******************************************************************************
  36.  
  37.  
  38.         TITLE    'SExpr# - Parse a simple expression'
  39.         
  40.         MACRO
  41.         SExpr#    &Expr,&Keyword
  42. .*
  43. .******************************************************************************
  44. .* SExpr# - Parse a simple expression                        (internal macro) *
  45. .*                                                                            *
  46. .* Input:  &Expr     = simple expression with the following syntax:           *
  47. .*                     <s-expr> ::= <cc>[.<sz>] | <ea> <cc>[.<sz>] <ea>       *
  48. .*                     <cc>     ::= EQ | NE | LE | LT | GE | GT | MI | PL |   *
  49. .*                                  HI | LS | LO | CC | CS | NZ | HS | VC |   *
  50. .*                                  VS                                        *
  51. .*         &Keyword  = a valid keyword delimiter of the form <keyword>        *
  52. .*                                                                            *
  53. .*         &SysToken = 1st token value of &Expr scanned by caller      (SETA) *
  54. .*         &SysTokStr= 1st token of &Expr scanned by caller            (SETC) *
  55. .*                                                                            *
  56. .*         &Cp       = &Expr scan pointer pinting after 1st token      (SETA) *
  57. .*         &CC#      = condition code table                            (SETC) *
  58. .*         &Extent#  = extent attribute on preceeding keyword          (SETC) *
  59. .*         &Debug#   = debug printing switch                           (SETA) *
  60. .*                                                                            *
  61. .* Output: &Cp       = &Expr scan pointer pointing after delimiter     (SETA) *
  62. .*         &SysToken = token value of &Expr delimiter                  (SETA) *
  63. .*         &SysTokStr= 1st token of &Expr delimiter                    (SETC) *
  64. .*         &Cmp      = condition code index                            (SETA) *
  65. .*         &Left#    = left <ea> if <s-expr> = <ea><cc><ea>            (SETC) *
  66. .*         &Right#   = right <ea> if <s-expr> = <ea><cc><ea>           (SETC) *
  67. .*         &Op#      = AND or OR delimiter or <null>                   (SETC) *
  68. .*         &Size#    = size attribute from <cc>[.<sz>]                 (SETC) *
  69. .*         &Extent#  = extent attribute on delimiter keyword           (SETC) *
  70. .******************************************************************************
  71. .*
  72.         GBLA        &Cp#                ; scan pointer
  73.         GBLA        &Cmp#            ; condition code index
  74.         GBLC        &Left#            ; left <ea> if <s-expr> = <ea><cc><ea>
  75.         GBLC        &Right#            ; right <ea> if <s-expr> = <ea><cc><ea>
  76.         GBLC        &CC#                ; condition code table
  77.         GBLC        &Op#                ; AND or OR delimiter or <null>
  78.         GBLC        &Size#            ; size attribute from <cc>[.<sz>]
  79.         GBLC        &Extent#            ; extent attribute on delimiter keyword
  80.         GBLA        &Debug#            ; debug printing switch
  81. .*
  82.         LCLC        &Token
  83.         LCLA        &p,&i,&First,&Last
  84. .*
  85. .* Initialization
  86. .*
  87.     &Cmp#:   SETA 0                ; Assume we only have a simple <cc>
  88.     &Size#:  SETC 'W'                ; The default size attribute is .W
  89.     &Op#:    SETC ''                ; If only <cc>, &Op# will be <null>
  90.     &Right#: SETC ''                ; And there would be no right operand
  91. .*
  92. .* Check for <s-expr> ::= <cc>[.<sz>]  This is (currently) only possible if the
  93. .* length of the incoming token is 2 (since all the cc's are two chars).  If we
  94. .* find the incoming token in the condition code table, then we assume we have
  95. .* <s-expr> ::= <cc>[.<sz>].  This means that an <ea> cannot start with an id
  96. .* that is the same as one of our <cc>'s.  It's that or we must escape the
  97. .* <cc>'s somehow.  
  98. .*
  99.     IF &Len(&SysTokStr) = 2 THEN        ; Possible <cc>
  100.       &Cmp#: SETA &Pos(&Concat(&UC(&SysTokStr), '.'), &CC#); Look it up
  101.       IF &Cmp# THEN                ; Have <s-expr> ::= <cc>[.<sz>]
  102.         &Left#: SETC ''                ; There is no left operand
  103.         IF &Expr[&Cp#:1] ≠ '.' THEN    ; Have explicit size attribute ?
  104.           &Cp#: SETA &Lex(&Expr, &Cp#)    ; No, scan the delimiter for caller
  105.         ELSE                        ; If explicit size attribute
  106.           &Size#: SETC &UC(&Expr[&Cp#+1:1]); Return it to caller
  107.           &Cp#:   SETA &Lex(&Expr, &Cp#+2) ; Scan delimiter for caller
  108.         ENDIF
  109.         IF &SysToken ≠ 0 GOTO .Exit    ; Exit if delimiter is not an identifier
  110.         &Token: SETC &UC(&SysTokStr)    ; If identifier, see if it is keyword
  111.         IF &Token = &Keyword THEN        ; Is it ?
  112.           IF &Expr[&Cp#:1] = '.' THEN    ; Yes, return <extent> to caller
  113.             &Extent#: SETC &UC(&Trim(&Expr[&Cp#+1:1]))
  114.             &Cp#:     SETA &Lex(&Expr, &Cp#+2)
  115.           ENDIF
  116.         ELSEIF (&Token = 'AND') OR (&Token = 'OR') THEN
  117.           &Op#: SETC &Token            ; Tell caller we have AND, OR delimiter
  118.           &Cp#: SETA &Lex(&Expr, &Cp#)
  119.         ENDIF
  120.         GOTO .Exit                    ; Skip over <ea> <cc> <ea> parsing
  121.       ENDIF
  122.     ENDIF
  123. .*
  124. .* At this point we assume we have <s-expr> ::= <ea> <cc>[.<sz>] <ea>. Everything
  125. .* from the start of the <s-expr> up to the <cc> is considered as part of the
  126. .* left-hand <ea>.  Keep scanning until we find the <cc>.  Everything to the
  127. .* left of the <cc> is placed in &Left# for the caller.  &Cmp# will be returned
  128. .* as the condition code table index corresponding to the <cc> we "crashed"
  129. .* into.  Note, that as in the simple <cc> case above, we assume that the <cc> 
  130. .* identifiers are reserved words and cannot be used in user expressions
  131. .* possibly comprising the <ea>'s.
  132. .*
  133.     &Left#: SETC &SysTokStr            ; The caller already scanned 1st token
  134.     &First: SETA &Cp#                ; "First" is not-quite right here!
  135. .*
  136.     WHILE &Cmp# = 0 DO                ; Loop until <cc> is scanned
  137.       &Last: SETA &Cp#                ; Assume next token will be the <cc>
  138.       &Cp#: SETA &Lex(&Expr, &Cp#)    ; Scan the next token
  139.       IF &SysToken = 0 THEN            ; If possible <cc> (an identifier token)
  140.         IF &Len(&SysTokStr) = 2 THEN    ; And the id length is 2 ==> possible <cc>
  141.           &Cmp#: SETA &Pos(&Concat(&UC(&SysTokStr), '.'), &CC#)
  142.          IF &Cmp# ≠ 0 THEN            ; Do we really have a <cc> ?
  143.            IF &Expr[&Cp#:1] = '.' THEN; Yes, scan size attribute if it's there
  144.             &Size#: SETC &UC(&Expr[&Cp#+1:1])
  145.             &Cp#:   SETA &Cp#+2
  146.            ENDIF
  147.          ENDIF
  148.         ENDIF
  149.       ELSEIF &SysToken = 30 THEN        ; If we hit end of expr, we have error
  150.         AERROR 'Invalid expression'
  151.         EXITM
  152.       ENDIF
  153.     ENDW
  154.     &Left#: SETC &Trim(&Concat(&Left#, &Expr[&First:&Last-&First+1]))
  155. .*
  156. .* Now scan the right-hand <ea> of <s-expr> ::= <ea> <cc>[.<sz>] <ea>.  This is
  157. .* similar to the left-hand scan above, except that here we keep scanning until
  158. .* we "crash" into an AND, or OR, a specified keyword (in the &Keyword macro
  159. .* parameter), or, of course, the end of line.  Note, care has to be taken here
  160. .* when detecting an AND or OR or the keyword.  We only want to accept these
  161. .* words when we think we are NOT nested inside an arbitrarily complex
  162. .* expression which could make up the <ea>.  Unlike the <cc>'s, we cannot make
  163. .* AND and OR reserved words, since they are already Assembler reserved words!
  164. .* To get around this problem, we count parentheses, and accept AND and OR (and
  165. .* the keyword) only if NOT nested inside parentheses.
  166. .*
  167.     &First: SETA &Cp#                ; Remember the start of the left <ea>
  168.     WHILE 1 DO                    ; Loop until we find delimiter
  169.       &Last: SETA &Cp#                ; Assume next token will be the delimiter
  170.       &Cp#: SETA &Lex(&Expr, &Cp#)    ; Scan it
  171.       IF &SysToken = 0 THEN            ; Have an identifier token ?
  172.         IF &p = 0 THEN                ; Yes, look at it only if not nested
  173.           &Token: SETC &UC(&SysTokStr)    ; If it isn't nested...
  174.           IF &Token = &Keyword THEN    ; Do we have the specified keyword ?
  175.             IF &Expr[&Cp#:1] = '.' THEN; Yes, return <extent> if present
  176.               &Extent#: SETC &UC(&Expr[&Cp#+1:1])
  177.             ENDIF
  178.             GOTO .SetLeft            ; Hop to return the left <ea>
  179.           ENDIF
  180.           IF (&Token = 'AND') OR (&Token = 'OR') THEN; If AND or OR
  181.             &Op#: SETC &Token        ; Return it to the caller
  182.             &Cp#: SETA &Lex(&Expr, &Cp#)
  183.             GOTO .SetLeft            ; Hop to return the left <ea>
  184.           ENDIF
  185.         ENDIF
  186.       ELSEIF &SysToken = 23 THEN        ; Have ")" ?
  187.         &p: SETA &p-1                ; Yes, decrement nesting count
  188.         IF &p < 0 GOTO .SetLeft        ; If unmatched paren, just set left <ea>
  189.       ELSEIF &SysToken = 22 THEN         ; Have ")" ?
  190.         &p:      SETA &p+1            ; Yes, increment nesting count
  191.       ELSEIF &SysToken = 30 THEN        ; If end of line...
  192.         GOTO .SetLeft                ; ...just set left <ea>
  193.       ENDIF
  194.     ENDW
  195. .SetLeft ANOP
  196.     &Right#: SETC &Trim(&Expr[&First:&Last-&First+1])
  197. .*
  198. .Exit ANOP
  199. .*
  200.      IF &Debug# THEN
  201.       IF &Cmp# ≠ 0 THEN
  202.         WriteLn '   ', &Left#, ' ', &CC#[&Cmp#:2], '.', &Size#, '(=', &Cmp#, \
  203.                 ') ', &Right#, ' Token="', &SysTokStr, '"(', &SysToken, ')', \
  204.                ' Extent="', &Extent#, '"' 
  205.       ENDIF
  206.      ENDIF
  207.      IF (&Left# ≠ '') AND (&Right# = '') THEN
  208.       AERROR 'Invalid Expression'
  209.       &Cmp#: SETA 0
  210.         ENDIF
  211. .*
  212.         ENDM
  213.  
  214.         
  215.         TITLE    'Expr# - Parse a "full" expression'
  216.         
  217.         MACRO
  218.         Expr#.&Ext &Expr,&True==,&False==,&JumpCond:A==1,&Keyword==
  219. .*
  220. .******************************************************************************
  221. .* Expr# - Parse a "full" expression                         (internal macro) *
  222. .*                                                                            *
  223. .* Input:  &Expr      = "full" expression with the following syntax:          *
  224. .*                      <expr>   ::= <s-expr> [<op> <s-expr>]                 *
  225. .*                      <op>     ::= AND | OR                                 *
  226. .*                      <s-expr> ::= <cc>[.<sz>] | <ea> <cc>[.<sz>] <ea>      *
  227. .*                      <cc>     ::= EQ | NE | LE | LT | GE | GT | MI | PL |  *
  228. .*                                   HI | LS | LO | CC | CS | NZ | HS | VC |  *
  229. .*                                   VS                                       *
  230. .*         &True      = label to branch to if <expr> is "true"                *
  231. .*         &False     = label to branch to if <expr> is "false"               *
  232. .*         &JumpCond  = 1 ==> branch to &True if <expr> is true               *
  233. .*                      0 ==> branch to &True if <expr> is false              *
  234. .*         &Keyword   = a valid keyword delimiter of the form <keyword>       *
  235. .*         &Ext       = extent for branches                                   *
  236. .*                                                                            *
  237. .*         &Extent#   = extent attribute on preceeding keyword         (SETC) *
  238. .*         &Debug#    = debug printing switch                          (SETA) *
  239. .*                                                                            *
  240. .* Output: &Cp        = &Expr scan pointer pointing after delimiter    (SETA) *
  241. .*         &SysToken  = token value of &Expr delimiter                 (SETA) *
  242. .*         &SysTokStr = 1st token of &Expr delimiter                   (SETC) *
  243. .*         &Extent#   = extent attribute on delimiter keyword (<ext>)  (SETC) *
  244. .*         &FalseUsed#=1==><ea> AND/OR <ea> w/ &JumpCond=1/0(see Test#)(SETA) *
  245. .*         &CC#       = condition code table (for <cc>)                (SETC) *
  246. .*         &NotCC#    = negated condition code table (for ¬<cc>)       (SETC) *
  247. .*         &RevCC#    = reverse condition code table (for reverse <cc>)(SETC) *
  248. .*         &NotRevCC# = negated reverse condition code table           (SETC) *
  249. .*                                                                            *
  250. .* Code:   For <s-expr> ::= <cc>[.<sz>] | <eaA> <cc>[.<sz>] <eaB>             *
  251. .*                                                                            *
  252. .*           &JumpCond=1: [CMP.<sz>      <eaB>,<eaA>]; <eaA> <cc>[.<sz>] <eaB>*
  253. .*                         B<cc>.<ext>   True                                 *
  254. .*                                                                            *
  255. .*           &JumpCond=0: [CMP.<sz>      <eaB>,<eaA>]; <eaA> <cc>[.<sz>] <eaB>*
  256. .*                         B¬<cc>.<ext>  True                                 *
  257. .*        ------------------------------------------------------------------- *
  258. .*        For <expr> ::= <s-expr1> AND <s-expr2>                              *
  259. .*                                                                            *
  260. .*           &JumpCond=1: [CMP.<sz>      <eaB1>,<eaA1>]                       *
  261. .*                         B¬<cc>.<ext>  False          ; Sets &FalseUsed#=1  *
  262. .*                        [CMP.<sz>      <eaB2>,<eaA2>]                       *
  263. .*                         B<cc>.<ext>   True                                 *
  264. .*                                                                            *
  265. .*           &JumpCond=0: [CMP.<sz>      <eaB1>,<eaA1>]                       *
  266. .*                         B¬<cc>.<ext>  True                                 *
  267. .*                        [CMP.<sz>      <eaB2>,<eaA2>]                       *
  268. .*                         B¬<cc>.<ext>  True                                 *
  269. .*        ------------------------------------------------------------------- *
  270. .*        For <expr> ::= <s-expr1> OR <s-expr2>                               *
  271. .*                                                                            *
  272. .*           &JumpCond=1: [CMP.<sz>      <eaB1>,<eaA1>]                       *
  273. .*                         B<cc>.<ext>   True                                 *
  274. .*                        [CMP.<sz>      <eaB2>,<eaA2>]                       *
  275. .*                         B<cc>.<ext>   True                                 *
  276. .*                                                                            *
  277. .*           &JumpCond=0: [CMP.<sz>      <eaB1>,<eaA1>]                       *
  278. .*                         B<cc>.<ext>   False          ; Sets &FalseUsed#=1  *
  279. .*                        [CMP.<sz>      <eaB2>,<eaA2>]                       *
  280. .*                         B¬<cc>.<ext>  True                                 *
  281. .*                                                                            *
  282. .*        Note, in the above code, B¬<cc> is a branch on the "false" or       *
  283. .*        inverse of the specified condition.  The compares are actually      *
  284. .*        generated in the order appropriate to their operands.  Thus the     *
  285. .*        operands could be reversed which would also cause the inverse       *
  286. .*        condition to be used for a branch.  Also note that the AND with     *
  287. .*        &JumpCond=1 and OR with &JumpCond=0 are the only cases which require*
  288. .*        a False label parameter to &Expr.  We set the global &FalseUsed#=1  *
  289. .*         to indicate to the caller that the false label must be defined for  *
  290. .*        that case.                                                    *
  291. .*                                                                            *
  292. .*        The following table may help show how the above code sequences were *
  293. .*        developed:                                               *
  294. .*                                                                            *
  295. .*            +-------------------------+-------------------------+           *
  296. .*            |       (a OR b)          |     (a AND b)          |           *
  297. .*            |        a                |        a             |           *
  298. .*            |        BT    true       |        BF    false     |           *
  299. .*            |        b                |        b             |           *
  300. .*            |        BT    true       |        BT    true          |           *
  301. .*            | false:                  | false:                  |           *
  302. .*            +-------------------------+-------------------------+           *
  303. .*            | ¬(a OR B) = (¬a AND ¬b) | ¬(a AND b) = (¬a OR ¬B) |           *
  304. .*            |        a                |        a             |           *
  305. .*            |        BT    false      |        BF    true             |           *
  306. .*            |        b                |        b             |           *
  307. .*            |        BF    true       |        BF    true          |           *
  308. .*            | false:                  | false:                  |           *
  309. .*            +-------------------------+-------------------------+           *
  310. .*                                                                            *
  311. .*        In the above table, ¬X implies &JumpCond=0 while X implies the       *
  312. .*         jump if true state, &JumpCond=1.  This is the way we think about    *
  313. .*        this stuff here!  The Test# generates the branch code.  It takes       *
  314. .*        care of generating the correct code which may involve reversing       *
  315. .*        tests if the operands that generate the compares for a and b are    *
  316. .*        not in the proper order.                                       *
  317. .******************************************************************************
  318. .*
  319.         PRINT    Push,NoMDir,NoMCall
  320. .*
  321.         GBLA        &Cp#                ; scan pointer
  322.         GBLA        &Cmp#            ; condition code index
  323.         GBLA        &FalseUsed#        ; <ea> AND(OR) <ea> with &JumpCond=1(0)
  324.         GBLC        &CC#                ; condition code table
  325.         GBLC        &NotCC#            ; negated condition code table
  326.         GBLC        &RevCC#            ; reverse condition code table
  327.         GBLC        &NotRevCC#        ; negated inverse condition code table
  328.         GBLC        &Left#            ; left <ea> if <s-expr> = <ea><cc><ea>
  329.         GBLC        &Right#            ; right <ea> if <s-expr> = <ea><cc><ea>
  330.         GBLC        &Op#                ; AND or OR delimiter or <null>
  331.         GBLC        &Size#            ; size attribute from <cc>[.<sz>]
  332.         GBLC        &Extent#            ; extent attribute on delimiter keyword
  333.         GBLA        &Debug#            ; debug printing switch
  334.         GBLA        &FirstIf            ; First time switch
  335. .*
  336.         LCLA        &lCmp
  337.         LCLC        &lSize,&lLeft,&lRight
  338. .*
  339. .* Show incoming <expr> if debug printing
  340. .*
  341.     IF &Debug# THEN
  342.       WriteLn
  343.       WriteLn    &Expr
  344.     ENDIF
  345. .*
  346. .* Set up condition code tables for Test#.  We do it here so that Test# doesn't
  347. .* have to.  Test# can be called twice for every Expr# call.  So this is more
  348. .* efficient.
  349. .*
  350. .* There are four tables here:
  351. .*    • main condition codes
  352. .*    • negate of the main condition codes
  353. .*    • main condition codes if the operands are reversed from what is legal asm
  354. .*    • negate of reversed condition codes
  355. .*
  356. .* It must be pointed out that reversing the operand comparisons is NOT the same
  357. .* as negating the condition codes.  For example, the negate of GE is LT, but the
  358. .* reverse of GE is LE.
  359. .*
  360.     IF NOT &FirstIf THEN
  361.       &CC#:       SETC 'EQ.NE.LE.LT.GE.GT.MI.PL.HI.LS.LO.CC.CS.NZ.HS.VC.VS.'
  362.       &NotCC#:    SETC 'NE.EQ.GT.GE.LT.LE.PL.MI.LS.HI.CC.LO.CC.EQ.CS.VS.VC.'
  363.       &RevCC#:    SETC 'EQ.NE.GE.GT.LE.LT.PL.MI.LO.**.HI.CS.CC.NZ.CS.VS.VC.'
  364.       &NotRevCC#: SETC 'NE.EQ.LT.LE.GT.GE.MI.PL.CC.LS.LS.CC.CS.EQ.CC.VC.VS.'
  365.       &FirstIf: SETA 0
  366.     ENDIF
  367. .*
  368. .* Scan the first <s-expr> in <s-expr> [<op> <s-expr>]
  369. .*
  370.     &Cp#:        SETA &Lex(&Expr, 1)        ; Scan the 1st token
  371.     &Extent#:    SETC &Default(&UC(&Ext), 'W'); Preset <extent> to &Ext
  372.     SExpr# &Expr,&Keyword                ; Parse the 1st <s-expr>
  373.     &lCmp:       SETA &Cmp#                ; Save info for 1st <s-expr>
  374.     &lSize:      SETC &Size#                ; All these are needed to gen code
  375.     &lLeft:      SETC &Left#                ; We copy them to locals in case
  376.     &lRight:     SETC &Right#            ; there is a right-hand <s-expr>
  377.     &FalseUsed#: SETA 0                    ; Assume we done have false labels
  378. .*
  379.     IF &Debug# THEN                    ; If debugging...
  380.       IF &Op# ≠ '' THEN                ; ...and we have AND or OR...
  381.         Writeln '   ', &Op#                ; ...show we have them
  382.       ENDIF
  383.     ENDIF
  384. .*
  385. .* Generate code: if we have  <s-expr> <op> <s-expr>, where <op> is an AND or OR
  386. .* then we must scan the right-hand <s-expr> prior to generating the code.  The
  387. .* actual code is generated by Test#.  But it is controlled from here to cause
  388. .* optimum code generation for the AND or OR condition.
  389. .*
  390.     IF &Op# = 'AND' THEN                ; Have and AND ?
  391.       SExpr# &Expr,&Keyword                ; Yes parse the right <s-expr>
  392.       IF &JumpCond THEN                    ; Branch to &True if expr is true ?
  393.         &FalseUsed#: SETA 1                ; Yes, indicate &False label used 
  394.         Test#.&lSize &lCmp,&lLeft,&lRight,&False,&Extent#,0 ; B<F>  False
  395.         Test#.&Size# &Cmp#,&Left#,&Right#,&True,&Extent#,1  ; B<T>  True
  396.       ELSE
  397.         Test#.&lSize &lCmp,&lLeft,&lRight,&True,&Extent#,0  ; B<F>  True
  398.         Test#.&Size# &Cmp#,&Left#,&Right#,&True,&Extent#,0  ; B<F>  True
  399.       ENDIF
  400.     ELSEIF &Op# = 'OR' THEN                ; Have an OR ?
  401.       SExpr# &Expr,&Keyword                ; Yes parse the right <s-expr>
  402.       IF &JumpCond THEN
  403.         Test#.&lSize &lCmp,&lLeft,&lRight,&True,&Extent#,1  ; B<T>  True
  404.         Test#.&Size# &Cmp#,&Left#,&Right#,&True,&Extent#,1  ; B<T>  True
  405.       ELSE
  406.         &FalseUsed#: SETA 1                ; Indicate &False label used 
  407.         Test#.&lSize &lCmp,&lLeft,&lRight,&False,&Extent#,1 ; B<T>  False
  408.         Test#.&Size# &Cmp#,&Left#,&Right#,&True,&Extent#,0  ; B<F>  True
  409.       ENDIF
  410.     ELSE
  411.       Test#.&lSize &lCmp,&lLeft,&lRight,&True,&Extent#,&JumpCond
  412.     ENDIF
  413. .*
  414. .Exit     PRINT     Pop
  415.         ENDM    
  416.  
  417.  
  418.         TITLE    'Test# - Generate expression code for a <s-expr>'
  419.         
  420.         MACRO
  421.         Test#.&Sz &Cmp#:A,&a,&b,&Dst,&Extent,&JumpCond:A
  422. .*
  423. .******************************************************************************
  424. .* Test# - Generate expression code for a <s-expr>           (internal macro) *
  425. .*                                                                            *
  426. .* Input:  &Cmp#      = condition code table index                            *
  427. .*         &a         = left operand (if <null>, then <s-expr>::=<cc>[.<sz>]) *
  428. .*         &b         = right operand                                         *
  429. .*         &Dst       = destination label for branches                        *
  430. .*         &Extent    = extent attribute for branches (<ext>)                 *
  431. .*         &JumpCond  = 1 ==> branch to &Dst if condition is true             *
  432. .*                      0 ==> branch to &Dst if condition is false            *
  433. .*         &Sz        = comparison size attribute (B, W, L)                   *
  434. .*                                                                            *
  435. .*         &CC#       = condition code table (for <cc>)                (SETC) *
  436. .*         &NotCC#    = negated condition code table (for ¬<cc>)       (SETC) *
  437. .*         &RevCC#    = reverse condition code table (for reverse <cc>)(SETC) *
  438. .*         &NotRevCC# = negated reverse condition code table           (SETC) *
  439. .*         &Debug#    = debug printing switch                          (SETA) *
  440. .*                                                                            *
  441. .* Code:   For <s-expr> ::= <cc>[.<sz>] | <eaA> <cc>[.<sz>] <eaB>             *
  442. .*                                                                            *
  443. .*           &JumpCond=1: [CMP.<sz>      <eaB>,<eaA>]; <eaA> <cc>[.<sz>] <eaB>*
  444. .*                         B<cc>.<ext>   True                                 *
  445. .*                                                                            *
  446. .*           &JumpCond=0: [CMP.<sz>      <eaB>,<eaA>]; <eaA> <cc>[.<sz>] <eaB>*
  447. .*                         B¬<cc>.<ext>  True                                 *
  448. .*                                                                            *
  449. .*         The operands are inverted (along with the condition code) under    *
  450. .*         the following conditions:                                          *
  451. .*                                                                            *
  452. .*           • <eaA> is a #<immediate>                                        *
  453. .*           • <eaB> is a register                                            *
  454. .*                                                                            *
  455. .*         These cases cause us to use the reverse and negated reverse        *
  456. .*          condition code tables.  Note that these tables are reguired because*
  457. .*         a reversed condition code is NOT the same as a negate of the       *
  458. .*          original condition code!                                   *
  459. .******************************************************************************
  460. .*
  461.         PRINT    Push,NoMDir,NoMCall
  462. .*
  463.         GBLC        &CC#                ; condition code table
  464.         GBLC        &NotCC#            ; negated condition code table
  465.         GBLC        &RevCC#            ; reverse condition code table
  466.         GBLC        &NotRevCC#        ; negated inverse condition code table
  467. .*
  468.         LCLC        &tRevCC            ; tmp to fool with &RevCC#
  469. .*
  470.     IF &Cmp# = 0 GOTO .Exit            ; If there are syntax errors, exit
  471. .*
  472.     IF &a = '' THEN                ; <s-expr> ::= <cc>[.<sz>]
  473.       IF &JumpCond THEN                ; Just do the branch
  474.           B&CC#[&Cmp#:2].&Extent     &Dst      
  475.       ELSE
  476.           B&NotCC#[&Cmp#:2].&Extent     &Dst
  477.       ENDIF
  478.     ELSEIF &a[1:1] = '#' THEN        ; <s-expr> ::= #<imm> <cc>[.<sz>] <eaB>
  479.           CMPI.&Sz  &a,&b
  480.       IF &JumpCond THEN
  481. .*        B&NotCC#[&Cmp#:2].&Extent     &Dst
  482.         &tRevCC: SETC &RevCC#[&Cmp#:2]
  483.         IF &tRevCC ≠ '**' THEN
  484.           B&tRevCC..&Extent     &Dst
  485.         ELSE
  486.           BHI.&Extent &Dst
  487.           BEQ.&Extent &Dst
  488.         ENDIF
  489.       ELSE
  490.           B&NotRevCC#[&Cmp#:2].&Extent     &Dst
  491.       ENDIF
  492.     ELSEIF &b[1:1] = '#' THEN        ; <s-expr> ::= <eaA> <cc>[.<sz>] #<imm>
  493.           CMPI.&Sz  &b,&a
  494.       IF &JumpCond THEN
  495.           B&CC#[&Cmp#:2].&Extent     &Dst
  496.       ELSE
  497.           B&NotCC#[&Cmp#:2].&Extent     &Dst
  498.       ENDIF
  499.     ELSEIF &SubStr(&Type(&a),1,3)='REG' THEN; <s-expr> ::= <reg> <cc>[.<sz>] <eaB>
  500.           CMP.&Sz   &b,&a
  501.       IF &JumpCond THEN
  502.           B&CC#[&Cmp#:2].&Extent     &Dst
  503.       ELSE
  504.           B&NotCC#[&Cmp#:2].&Extent     &Dst
  505.       ENDIF
  506.     ELSEIF &SubStr(&Type(&b),1,3)='REG' THEN; <s-expr> ::= <eaA> <cc>[.<sz>] <reg>
  507.           CMP.&Sz   &a,&b
  508.       IF &JumpCond THEN
  509. .*        B&NotCC#[&Cmp#:2].&Extent     &Dst
  510.         &tRevCC: SETC &RevCC#[&Cmp#:2]
  511.         IF &tRevCC ≠ '**' THEN
  512.           B&tRevCC..&Extent     &Dst
  513.         ELSE
  514.           BHI.&Extent &Dst
  515.           BEQ.&Extent &Dst
  516.         ENDIF
  517.       ELSE
  518.           B&NotRevCC#[&Cmp#:2].&Extent     &Dst
  519.       ENDIF
  520.     ELSE                            ; <s-expr> ::= <eaA> <cc>[.<sz>] <eaB>
  521.         CMP.&Sz   &b,&a
  522.       IF &JumpCond THEN
  523.           B&CC#[&Cmp#:2].&Extent     &Dst
  524.       ELSE
  525.           B&NotCC#[&Cmp#:2].&Extent     &Dst
  526.       ENDIF
  527.     ENDIF
  528. .*
  529. .Exit    PRINT    Pop
  530.         ENDM
  531.  
  532.  
  533.         TITLE    'GoTo# - (Conditional) transfer of control statement'
  534.         
  535.         MACRO
  536.         GoTo#.&Ext &Where
  537. .*
  538. .******************************************************************************
  539. .* GoTo# - (Conditional) transfer of control statement                        *
  540. .*                                                                            *
  541. .* Call: Goto#.<ext> [If[#] <expr> Then.<ext>] <label>                        *
  542. .*                                                                            *
  543. .* Code: [<expr>]                                                             *
  544. .*        B<cc>.<ext>  <label>                                                *
  545. .******************************************************************************
  546. .*
  547.         PRINT    Push,NoMDir,NoMCall
  548. .*
  549.         GBLA        &Cp#                ; scan pointer
  550.         GBLA        &FalseUsed#        ; <ea> AND(OR) <ea> with &JumpCond=1(0)
  551. .*
  552.         LCLC        &Opnds,&S
  553.         LCLA        &i,&l
  554. .*
  555. .* We must look at first token to see the form of GoTo# we got
  556. .*
  557.     &Opnds: SETC &Where                ; Copy operands where we can edit it
  558.     &Cp#:   SETA &Lex(&Opnds, 1)        ; Scan 1st token
  559. .*
  560. .* For GoTo# If# <expr> Then.<ext> <label>, generate a conditional branch
  561. .*
  562.     IF &SysToken = 0 THEN            ; Do we have an idtentifier ?
  563.       &S: SETC &UC(&SysTokStr)        ; Yes, see if it is an "IF" or "IF#"
  564.       IF (&S = 'IF') OR (&S = 'IF#') THEN; Is it ?
  565.         &Opnds: SETC &Opnds[&Cp#:255]    ; Yes ==> If[#] <expr> THEN.<ext> <label>
  566.         &l: SETA &Len(&Opnds)        ; Extract the <label>
  567.         &i: SETA -&ScanEQ(' ', &Opnds, -&l); First scan to 1st blank before <label>
  568.         IF &i = &l THEN
  569.           AERROR 'GOTO# label missing'
  570.          GOTO .Exit
  571.         ENDIF
  572.         Expr#.&Ext &Opnds,True=&Opnds[&l-&i+1:&i],False=%L%&SysNdx,JumpCond=1,Keyword=THEN
  573.         IF NOT &FalseUsed# GOTO .Exit    ; If we need False label, generate it
  574. %L%&SysNdx
  575.         GOTO .Exit                    ; Wasn't that simple?
  576.       ENDIF
  577.     ENDIF
  578. .*
  579. .* For GoTo# <label>, generate a simple branch
  580. .*
  581.           BRA.&Ext  &Opnds
  582. .*
  583. .Exit    Print    Pop
  584.         ENDM
  585.  
  586.  
  587.         TITLE    'If# - Multi-way decision'
  588.         
  589.         MACRO
  590.         If#        &Expr
  591. .*
  592. .******************************************************************************
  593. .* If# - Multi-way decision                                                   *
  594. .*                                                                            *
  595. .* Call:   If# <expr> Then[.<ext>]                     ***                    *
  596. .*         ElseIf#[.<ext1>] <expr> THEN[.<ext2>]                              *
  597. .*         Else#[.<ext>]                                                      *
  598. .*         EndIf#                                                             *
  599. .*                                                                            *
  600. .* Input:  &StkPtr#            = statment nesting stack pointer        (SETA) *
  601. .*                                                                            *
  602. .* Output: &StkPtr#            = statment nesting stack ptr incremented(SETA) *
  603. .*         &KindStk#[&StkPtr#] = statement nesting kind = 1            (SETA) *
  604. .*         &LblStk#[&StkPtr#]  = label suffix to next If# section      (SETC) *
  605. .*         &Lbl1Stk#[&StkPtr#] = label suffix to EndIf# for ElseIf#    (SETC) *
  606. .*                                                                            *
  607. .* Code:   * The following is generated by If# (HERE IN THIS MACRO)           *
  608. .*                  <expr>                                                    *
  609. .*                  B¬<cc>.<ext>     %L%xxx1                                  *
  610. .*         ------------------------------------------------------------------ *
  611. .*         * The following is generated by ElseIf#                            *
  612. .*                  BRA.<ext1>       %E%xxxx                                  *
  613. .*         %L%xxx1  <expr>                                                    *
  614. .*                  B¬<cc>.<ext2>    %L%xxx2                                  *
  615. .*         ------------------------------------------------------------------ *
  616. .*         * The following is generated by Else#                              *
  617. .*                  BRA.<ext>        %L%xxx3                                  *
  618. .*         %L%xxx2                                                            *
  619. .*         ------------------------------------------------------------------ *
  620. .*         * The following is generated by EndIf#                             *
  621. .*         %L%xxxx3                                                           *
  622. .*         %E%xxxx                                  ; only if ElseIf# used    *
  623. .******************************************************************************
  624. .*
  625.         PRINT    Push,NoMDir,NoMCall
  626. .*
  627.         GBLA        &StkPtr#            ; statment nesting stack pointer
  628.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  629.         GBLC        &LblStk#[25]        ; label suffix to next If# section
  630.         GBLC        &Lbl1Stk#[25]        ; label suffix to EndIf# for ElseIf#
  631.         GBLA        &FalseUsed#        ; <ea> AND(OR) <ea> with &JumpCond=1(0)
  632. .*
  633. .* Push new statement status on the stack
  634. .*
  635.     IF &StkPtr# = 25 THEN
  636.       AERROR 'Too many nested statements'
  637.       GOTO .Exit
  638.     ENDIF
  639. .*
  640.     &StkPtr#:            SETA &StkPtr#+1; Update stack ptr
  641.     &LblStk#[&StkPtr#]:  SETC &SysNdx     ; Label suffix to Else#, ElseIf#, EndIf#
  642.     &Lbl1Stk#[&StkPtr#]: SETC ''         ; No ElseIf# yet
  643.     &KindStk#[&StkPtr#]: SETA 1         ; Kind = 1 for If# statements
  644. .*
  645. .* Generate code to branch on false to next ElseIf#, Else#, or EndIf#
  646. .*
  647.     Expr# &Expr,True=%L%&SysNdx,False=%F%&SysNdx,JumpCond=0,Keyword=THEN
  648.     IF &FalseUsed# THEN                 ; If we need False label, generate it
  649. %F%&SysNdx
  650.     ENDIF
  651. .*
  652. .Exit    Print    Pop
  653.         ENDM
  654.  
  655.  
  656.         TITLE    'ElseIf# - Additional case testing in If# statement'
  657.         
  658.         MACRO
  659.         ElseIf#.&Ext &Expr
  660. .*
  661. .******************************************************************************
  662. .* ElseIf# - Additional case testing in If# statement                         *
  663. .*                                                                            *
  664. .* Call:   If# <expr> Then[.<ext>]                                            *
  665. .*         ElseIf#[.<ext1>] <expr> THEN[.<ext2>]       ***                    *
  666. .*         Else#[.<ext>]                                                      *
  667. .*         EndIf#                                                             *
  668. .*                                                                            *
  669. .* Input:  &StkPtr#            = statment nesting stack pointer        (SETA) *
  670. .*         &KindStk#[&StkPtr#] = statement nesting kind = 1            (SETA) *
  671. .*         &LblStk#[&StkPtr#]  = label suffix to next If# section      (SETC) *
  672. .*         &Lbl1Stk#[&StkPtr#] = label suffix to EndIf# for ElseIf#    (SETC) *
  673. .*                                                                            *
  674. .* Output: &LblStk#[&StkPtr#]  = label suffix to next If# section      (SETC) *
  675. .*         &Lbl1Stk#[&StkPtr#] = label suffix to EndIf# for ElseIf#    (SETC) *
  676. .*                                                                            *
  677. .* Code:   * The following is generated by If#                                *
  678. .*                  <expr>                                                    *
  679. .*                  B¬<cc>.<ext>     %L%xxx1                                  *
  680. .*         ------------------------------------------------------------------ *
  681. .*         * The following is generated by ElseIf# (HERE IN THIS MACRO)       *
  682. .*                  BRA.<ext1>       %E%xxxx                                  *
  683. .*         %L%xxx1  <expr>                                                    *
  684. .*                  B¬<cc>.<ext2>    %L%xxx2                                  *
  685. .*         ------------------------------------------------------------------ *
  686. .*         * The following is generated by Else#                              *
  687. .*                  BRA.<ext>        %L%xxx3                                  *
  688. .*         %L%xxx2                                                            *
  689. .*         ------------------------------------------------------------------ *
  690. .*         * The following is generated by EndIf#                             *
  691. .*         %L%xxxx3                                                           *
  692. .*         %E%xxxx                                  ; only if ElseIf# used    *
  693. .******************************************************************************
  694. .*
  695.         PRINT    Push,NoMDir,NoMCall
  696. .*
  697.         GBLA        &StkPtr#            ; statment nesting stack pointer
  698.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  699.         GBLC        &LblStk#[25]        ; label suffix to next If# section
  700.         GBLC        &Lbl1Stk#[25]        ; label suffix to EndIf# for ElseIf#
  701.         GBLA        &FalseUsed#        ; <ea> AND(OR) <ea> with &JumpCond=1(0)
  702. .*
  703. .* Validate that the ElseIf# is nested in an If# statement
  704. .*
  705.     IF &StkPtr# = 0 THEN
  706.       AERROR 'ELSE# not nested in an IF#'
  707.       GOTO .Exit
  708.     ENDIF
  709.     IF &KindStk#[&StkPtr#] ≠ 1 THEN
  710.       AERROR 'ELSE# not nested in an IF#'
  711.       GOTO .Exit
  712.     ENDIF
  713. .*
  714. .* All ElseIf# branch arounds go to a single label defined by EndIf#
  715. .*
  716.     IF &Lbl1Stk#[&StkPtr#] = '' THEN
  717.       &Lbl1Stk#[&StkPtr#]: SETC &SysNdx
  718.           BRA.&Ext  %E%&SysNdx
  719.     ELSE
  720.           BRA.&Ext  %E%&Lbl1Stk#[&StkPtr#]
  721.     ENDIF
  722. .*
  723. .* Define the label branched to by the previous If# or ElseIf#
  724. .*
  725. %L%&LblStk#[&StkPtr#]
  726. .*
  727. .* Generate code to branch on false to next ElseIf#, Else#, or EndIf#
  728. .*
  729.      Expr# &Expr,True=%L%&SysNdx,False=%F%&SysNdx,JumpCond=0,Keyword=THEN
  730.     &LblStk#[&StkPtr#]: SETC &SysNdx
  731.     IF &FalseUsed# THEN                 ; If we need False label, generate it
  732. %F%&SysNdx
  733.     ENDIF
  734. .*
  735. .Exit    PRINT    Pop
  736.         ENDM
  737.  
  738.  
  739.         TITLE    'Else# - Final alternative for If# statement'
  740.         
  741.         MACRO
  742.         Else#.&Ext
  743. .*
  744. .******************************************************************************
  745. .* Else# - Final alternative for If# statement                                *
  746. .*                                                                            *
  747. .* Call:   If# <expr> Then[.<ext>]                                            *
  748. .*         ElseIf#[.<ext1>] <expr> THEN[.<ext2>]                              *
  749. .*         Else#[.<ext>]                               ***                    *
  750. .*         EndIf#                                                             *
  751. .*                                                                            *
  752. .* Input:  &StkPtr#            = statment nesting stack pointer        (SETA) *
  753. .*         &KindStk#[&StkPtr#] = statement nesting kind = 1            (SETA) *
  754. .*         &LblStk#[&StkPtr#]  = label suffix to next If# section      (SETC) *
  755. .*                                                                            *
  756. .* Output: &LblStk#[&StkPtr#]  = label suffix to EndIf#                (SETC) *
  757. .*                                                                            *
  758. .* Code:   * The following is generated by If#                                *
  759. .*                  <expr>                                                    *
  760. .*                  B¬<cc>.<ext>     %L%xxx1                                  *
  761. .*         ------------------------------------------------------------------ *
  762. .*         * The following is generated by ElseIf#                            *
  763. .*                  BRA.<ext1>       %E%xxxx                                  *
  764. .*         %L%xxx1  <expr>                                                    *
  765. .*                  B¬<cc>.<ext2>    %L%xxx2                                  *
  766. .*         ------------------------------------------------------------------ *
  767. .*         * The following is generated by Else# (HERE IN THIS MACRO)         *
  768. .*                  BRA.<ext>        %L%xxx3                                  *
  769. .*         %L%xxx2                                                            *
  770. .*         ------------------------------------------------------------------ *
  771. .*         * The following is generated by EndIf#                             *
  772. .*         %L%xxxx3                                                           *
  773. .*         %E%xxxx                                  ; only if ElseIf# used    *
  774. .******************************************************************************
  775. .*
  776.         PRINT    Push,NoMDir,NoMCall
  777. .*
  778.         GBLA        &StkPtr#            ; statment nesting stack pointer
  779.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  780.         GBLC        &LblStk#[25]        ; label suffix to next If# section
  781. .*
  782. .* Validate that the Else# is nested in an If# statement
  783. .*
  784.     IF &StkPtr# = 0 THEN
  785.       AERROR 'ELSE# not nested in an IF#'
  786.       GOTO .Exit
  787.     ENDIF
  788.     IF &KindStk#[&StkPtr#] ≠ 1 THEN
  789.       AERROR 'ELSE# not nested in an IF#'
  790.       GOTO .Exit
  791.     ENDIF
  792. .*
  793. .* Generate final branch-around and define label for previous If# or ElseIf#
  794. .*
  795.           BRA.&Ext  %L%&SysNdx
  796. %L%&LblStk#[&StkPtr#]
  797. .*
  798.     &LblStk#[&StkPtr#]: SETC &SysNdx
  799. .*
  800. .Exit     Print    Pop
  801.         ENDM
  802.  
  803.  
  804.         TITLE    'EndIf# - End of If# statement'
  805.         
  806.         MACRO
  807.         EndIf#
  808. .*
  809. .******************************************************************************
  810. .* EndIf# - End of If# statement                                              *
  811. .*                                                                            *
  812. .* Call:   If# <expr> Then[.<ext>]                                            *
  813. .*         ElseIf#[.<ext1>] <expr> THEN[.<ext2>]                              *
  814. .*         Else#[.<ext>]                                                      *
  815. .*         EndIf#                                      ***                    *
  816. .*                                                                            *
  817. .* Input:  &StkPtr#            = statment nesting stack pointer        (SETA) *
  818. .*         &KindStk#[&StkPtr#] = statement nesting kind = 1            (SETA) *
  819. .*         &LblStk#[&StkPtr#]  = label suffix to next If# section      (SETC) *
  820. .*         &Lbl1Stk#[&StkPtr#] = label suffix to EndIf# for ElseIf#    (SETC) *
  821. .*                                                                            *
  822. .* Output: &StkPtr#            = statment nesting stack ptr decremented(SETA) *
  823. .*                                                                            *
  824. .* Code:   * The following is generated by If#                                *
  825. .*                  <expr>                                                    *
  826. .*                  B¬<cc>.<ext>     %L%xxx1                                  *
  827. .*         ------------------------------------------------------------------ *
  828. .*         * The following is generated by ElseIf#                            *
  829. .*                  BRA.<ext1>       %E%xxxx                                  *
  830. .*         %L%xxx1  <expr>                                                    *
  831. .*                  B¬<cc>.<ext2>    %L%xxx2                                  *
  832. .*         ------------------------------------------------------------------ *
  833. .*         * The following is generated by Else#                              *
  834. .*                  BRA.<ext>        %L%xxx3                                  *
  835. .*         %L%xxx2                                                            *
  836. .*         ------------------------------------------------------------------ *
  837. .*         * The following is generated by EndIf# (HERE IN THIS MACRO)        *
  838. .*         %L%xxxx3                                                           *
  839. .*         %E%xxxx                                  ; only if ElseIf# used    *
  840. .******************************************************************************
  841. .*
  842.         PRINT    Push,NoMDir,NoMCall
  843. .*
  844.         GBLA        &StkPtr#            ; statment nesting stack pointer
  845.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  846.         GBLC        &LblStk#[25]        ; label suffix to next If# section
  847.         GBLC        &Lbl1Stk#[25]        ; label suffix to EndIf# for ElseIf#
  848. .*
  849. .* Validate that the ElseIf# is nested in an If# statement
  850. .*
  851.     IF &StkPtr# = 0 THEN
  852.       AERROR 'ENDIF# does not end an IF#'
  853.       GOTO .Exit
  854.     ENDIF
  855.     IF &KindStk#[&StkPtr#] ≠ 1 THEN
  856.       AERROR 'ENDIF# does not end an IF#'
  857.       GOTO .Exit
  858.     ENDIF
  859. .*
  860. .* Define final label branched to by If# or Else#
  861. .*
  862. %L%&LblStk#[&StkPtr#]
  863. .*
  864. .* If we had any ElseIf#'s, define their branch-around label
  865. .*
  866.     IF &Lbl1Stk#[&StkPtr#] ≠ '' THEN
  867. %E%&Lbl1Stk#[&StkPtr#]
  868.     ENDIF
  869. .*
  870. .* Pop the If# statement off the statement nesting stack
  871. .*
  872.     &StkPtr#: SETA &StkPtr#-1
  873. .*
  874. .Exit    PRINT    Pop
  875.         ENDM
  876.  
  877.  
  878.         TITLE    'Repeat# - Loop control statement'
  879.         
  880.         MACRO
  881. &Lbl        Repeat#
  882. .*
  883. .******************************************************************************
  884. .* Repeat# - Loop control statement                                           *
  885. .*                                                                            *
  886. .* Call:   Repeat#                                     ***                    *
  887. .*         Until#.<ext> {<expr> | False}                                      *
  888. .*                                                                            *
  889. .* Input:  &StkPtr#            = statment nesting stack pointer        (SETA) *
  890. .*                                                                            *
  891. .* Output: &StkPtr#            = statment nesting stack ptr incremented(SETA) *
  892. .*         &KindStk#[&StkPtr#] = statement nesting kind = 2            (SETA) *
  893. .*         &LblStk#[&StkPtr#]  = Repeat# label for Cycle# and Leave#   (SETC) *
  894. .*         &Lbl1Stk#[&StkPtr#] = label suffix for Leave# (<null>)      (SETC) *
  895. .*         &Lbl2Stk#[&StkPtr#] = label suffix for Cycle# (<null>)      (SETC) *
  896. .*                                                                            *
  897. .* Code:   * The following is generated by Repeat# (HERE IN THIS MACRO)       *
  898. .*         Lbl:  (user's label of %L%xxxx)                                    *
  899. .*         ------------------------------------------------------------------ *
  900. .*         * The following is generated by Until#                             *
  901. .*         %C%xxxx  EQU              Lbl      ; If Until False and Cycle      *
  902. .*                  BRA.<ext>        Lbl                                      *
  903. .*                                                                            *
  904. .*         %C%xxxx  <expr>                    ; If Until <expr>               *
  905. .*                  B¬<cc>.<ext>     Lbl                                      *
  906. .*         %E%xxxx                                                            *
  907. .******************************************************************************
  908. .*
  909.         PRINT    Push,NoMDir,NoMCall
  910. .*
  911.         GBLA        &StkPtr#            ; statment nesting stack pointer
  912.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  913.         GBLC        &LblStk#[25]        ; Repeat# label for Cycle# and Leave#
  914.         GBLC        &Lbl1Stk#[25]        ; label suffix for Leave#
  915.         GBLC        &Lbl2Stk#[25]        ; label suffix for Cycle#
  916. .*
  917. .* Push new statement status on the stack
  918. .*
  919.     IF &StkPtr# = 25 THEN
  920.       AERROR 'Too many nested statements'
  921.       GOTO .Exit
  922.     ENDIF
  923. .*
  924.     &StkPtr#: SETA &StkPtr#+1        ; Update stack ptr
  925.     IF &Lbl = '' THEN                ; Remember label for Until#, Cycle#, Leave#
  926.       &LblStk#[&StkPtr#]: SETC &Concat('%L%', &SysNdx)
  927.     ELSE
  928.       &LblStk#[&StkPtr#]: SETC &Lbl
  929.     ENDIF
  930.     &Lbl1Stk#[&StkPtr#]: SETC ''        ; No Leave# yet
  931.     &Lbl2Stk#[&StkPtr#]: SETC ''        ; No Cycle# yet
  932.     &KindStk#[&StkPtr#]: SETA 2        ; Kind = 2 for Repeat# statements
  933. .*
  934. .* Define label for top of loop
  935. .*
  936. &LblStk#[&StkPtr#]
  937. .*
  938. .Exit    PRINT    Pop
  939.         ENDM
  940.  
  941.  
  942.         TITLE    'Until# - End of Repeat# statement'
  943.         
  944.         MACRO
  945.         Until#.&Ext &Expr
  946. .*
  947. .******************************************************************************
  948. .* Until# - End of Repeat# statement                                          *
  949. .*                                                                            *
  950. .* Call:   Repeat#                                                            *
  951. .*         Until#.<ext> {<expr> | False}               ***                    *
  952. .*                                                                            *
  953. .* Input:  &StkPtr#            = statment nesting stack ptr incremented(SETA) *
  954. .*         &KindStk#[&StkPtr#] = statement nesting kind = 2            (SETA) *
  955. .*         &LblStk#[&StkPtr#]  = Repeat# label for Cycle# and Leave#   (SETC) *
  956. .*         &Lbl1Stk#[&StkPtr#] = label suffix for Leave#               (SETC) *
  957. .*         &Lbl2Stk#[&StkPtr#] = label suffix for Cycle#               (SETC) *
  958. .*                                                                            *
  959. .* Output: &StkPtr#            = statment nesting stack ptr decremented(SETA) *
  960. .*                                                                            *
  961. .* Code:   * The following is generated by Repeat#                            *
  962. .*         Lbl:  (user's label of %L%xxxx)                                    *
  963. .*         ------------------------------------------------------------------ *
  964. .*         * The following is generated by Until# (HERE IN THIS MACRO)        *
  965. .*         %C%xxxx  EQU              Lbl      ; If Until False and Cycle      *
  966. .*                  BRA.<ext>        Lbl                                      *
  967. .*                                                                            *
  968. .*         %C%xxxx  <expr>                    ; If Until <expr>               *
  969. .*                  B¬<cc>.<ext>     Lbl                                      *
  970. .*         %E%xxxx                                                            *
  971. .******************************************************************************
  972. .*
  973.         PRINT    Push,NoMDir,NoMCall
  974. .*
  975.         GBLA        &StkPtr#            ; statment nesting stack pointer
  976.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  977.         GBLC        &LblStk#[25]        ; Repeat# label for Cycle# and Leave#
  978.         GBLC        &Lbl1Stk#[25]        ; label suffix for Leave#
  979.         GBLC        &Lbl2Stk#[25]        ; label suffix for Cycle#
  980.         GBLA        &FalseUsed#        ; <ea> AND(OR) <ea> with &JumpCond=1(0)
  981. .*
  982. .* Validate that the Until# is nested in an Repeat# statement
  983. .*
  984.     IF &StkPtr# = 0 THEN
  985.       AERROR 'UNTIL# does not end a REPEAT#'
  986.       GOTO .Exit
  987.     ENDIF
  988.     IF &KindStk#[&StkPtr#] ≠ 2 THEN
  989.       AERROR 'UNTIL# does not end a REPEAT#'
  990.       GOTO .Exit
  991.     ENDIF
  992. .*
  993. .* Until# False requires special processing
  994. .*
  995.     IF &UC(&Expr) = 'FALSE' THEN
  996.       IF &Lbl2Stk#[&StkPtr#] ≠ '' THEN        ; Equate cycle label to top of loop
  997. %C%&Lbl2Stk#[&StkPtr#] EQU      &LblStk#[&StkPtr#]
  998.       ENDIF
  999.           BRA.&Ext  &LblStk#[&StkPtr#]
  1000.       GOTO .L1
  1001.     ENDIF
  1002. .*
  1003. .* If there was a Cycle# statement, define the Cycle# label prior to the <expr>
  1004. .*
  1005.     IF &Lbl2Stk#[&StkPtr#] ≠ '' THEN
  1006. %C%&Lbl2Stk#[&StkPtr#]
  1007.     ENDIF
  1008. .*
  1009. .* Generate code to conditionally branch to top of the loop
  1010. .*
  1011.     Expr#.&Ext &Expr,True=&LblStk#[&StkPtr#],False=%F%&SysNdx,JumpCond=0
  1012.     IF &FalseUsed# THEN                 ; If we need False label, generate it
  1013. %F%&SysNdx
  1014.     ENDIF
  1015. .*
  1016. .* If there was a Leave# statement, define the Leave# label now
  1017. .*
  1018. .L1    IF &Lbl1Stk#[&StkPtr#] ≠ '' THEN
  1019. %E%&Lbl1Stk#[&StkPtr#]
  1020.       ENDIF
  1021. .*
  1022. .* Pop the Repeat# statement off the statement nesting stack
  1023. .*
  1024.     &StkPtr#: SETA &StkPtr#-1
  1025. .*
  1026. .Exit    PRINT    Pop
  1027.         ENDM
  1028.  
  1029.  
  1030.         TITLE    'While# - Loop control statement'
  1031.         
  1032.         MACRO
  1033. &Lbl        While#    &Expr
  1034. .*
  1035. .******************************************************************************
  1036. .* While# - Loop control statement                                            *
  1037. .*                                                                            *
  1038. .* Call:   While# {<expr> | True} DO.<ext>             ***                    *
  1039. .*         EndW#                                                              *
  1040. .*                                                                            *
  1041. .* Input:  &StkPtr#            = statment nesting stack pointer        (SETA) *
  1042. .*                                                                            *
  1043. .* Output: &StkPtr#            = statment nesting stack ptr incremented(SETA) *
  1044. .*         &KindStk#[&StkPtr#] = statement nesting kind = 3            (SETA) *
  1045. .*         &LblStk#[&StkPtr#]  = While# label for Cycle# and Leave#    (SETC) *
  1046. .*         &Lbl1Stk#[&StkPtr#] = label suffix for Leave#               (SETC) *
  1047. .*         &Lbl2Stk#[&StkPtr#] = Extent attribute on DO                (SETC) *
  1048. .*                                                                            *
  1049. .* Code:   * The following is generated by While# (HERE IN THIS MACRO)        *
  1050. .*         Lbl                                    ; If While# True            *
  1051. .*                                                                            *
  1052. .*         Lbl      <expr>                        ; If While# <expr>          *
  1053. .*                  B¬<cc>.<ext>   %E%xxxx                                    *
  1054. .*         ------------------------------------------------------------------ *
  1055. .*         * The following is generated by EndW#                              *
  1056. .*                  BRA.<ext>     Lbl                                         *
  1057. .*         %E%xxxx                                                            *
  1058. .******************************************************************************
  1059. .*
  1060.         PRINT    Push,NoMDir,NoMCall
  1061. .*
  1062.         GBLA        &StkPtr#            ; statment nesting stack pointer
  1063.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  1064.         GBLC        &LblStk#[25]        ; While# label for Cycle# and Leave#
  1065.         GBLC        &Lbl1Stk#[25]        ; label suffix for Leave# and While# 
  1066.         GBLC        &Lbl2Stk#[25]        ; extent attribute on DO
  1067.         GBLC        &Extent#            ; extent attribute on DO from Expr#
  1068.         GBLA        &FalseUsed#        ; <ea> AND(OR) <ea> with &JumpCond=1(0)
  1069. .*
  1070.         LCLA        &i
  1071. .*
  1072. .* Push new statement status on the stack
  1073. .*
  1074.     IF &StkPtr# = 25 THEN
  1075.       AERROR 'Too many nested statements'
  1076.       GOTO .Exit
  1077.     ENDIF
  1078. .*
  1079.     &StkPtr#: SETA &StkPtr#+1        ; Update stack ptr
  1080.     IF &Lbl = '' THEN                ; Remember label for Until#, Cycle#, Leave#
  1081.       &LblStk#[&StkPtr#]: SETC &Concat('%L%', &SysNdx)
  1082.     ELSE
  1083.       &LblStk#[&StkPtr#]: SETC &Lbl
  1084.     ENDIF
  1085.     &Lbl1Stk#[&StkPtr#]: SETC &SysNdx    ; We always need a "leave" label
  1086.     &KindStk#[&StkPtr#]: SETA 3        ; Kind = 3 for While# statements
  1087. .*
  1088. .* Define label for top of loop
  1089. .*
  1090. &LblStk#[&StkPtr#]
  1091. .*
  1092. .* If While# True Do no other code needs to be generated.  But if we have
  1093. .* While# <expr> DO generate code to conditionally branch out of the loop.
  1094. .*
  1095.     &i: SETA &Lex(&Expr, 1)
  1096.     IF &SysToken = 0 THEN
  1097.       IF &UC(&SysTokStr) = 'TRUE' THEN
  1098.         &i: SETA &Lex(&expr, &i)
  1099.         IF &SysToken = 0 THEN
  1100.           IF &UC(&SysTokStr) = 'DO' THEN
  1101.            IF &Expr[&i:1] = '.' THEN
  1102.              &Lbl2Stk#[&StkPtr#]: SETC &UC(&Expr[&i+1:1])
  1103.            ELSE
  1104.              &Lbl2Stk#[&StkPtr#]: SETC 'W'
  1105.            ENDIF
  1106.            GOTO .Exit
  1107.          ENDIF
  1108.         ENDIF
  1109.       ENDIF
  1110.     ENDIF
  1111. .*
  1112.     Expr# &Expr,True=%E%&SysNdx,False=%F%&SysNdx,JumpCond=0,Keyword=DO
  1113.     &Lbl2Stk#[&StkPtr#]: SETC &Extent#    ; Remember <ext> for EndW#
  1114.     IF &FalseUsed# THEN                 ; If we need False label, generate it
  1115. %F%&SysNdx
  1116.     ENDIF
  1117. .*
  1118. .Exit    PRINT    Pop
  1119.         ENDM
  1120.  
  1121.  
  1122.         TITLE    'EndW# - End of Repeat# statement'
  1123.         
  1124.         MACRO
  1125.         EndW#
  1126. .*
  1127. .******************************************************************************
  1128. .* EndW# - End of Repeat# statement                                           *
  1129. .*                                                                            *
  1130. .* Call:   While# {<expr> | True} DO.<ext>                                    *
  1131. .*         EndW#                                       ***                    *
  1132. .*                                                                            *
  1133. .* Input:  &StkPtr#            = statment nesting stack ptr incremented(SETA) *
  1134. .*         &KindStk#[&StkPtr#] = statement nesting kind = 2            (SETA) *
  1135. .*         &LblStk#[&StkPtr#]  = While# label for Cycle# and Leave#    (SETC) *
  1136. .*         &Lbl1Stk#[&StkPtr#] = label suffix for Leave# and While#    (SETC) *
  1137. .*         &Lbl2Stk#[&StkPtr#] = Extent attribute on DO                (SETC) *
  1138. .*                                                                            *
  1139. .* Output: &StkPtr#            = statment nesting stack ptr decremented(SETA) *
  1140. .*                                                                            *
  1141. .* Code:   * The following is generated by While#                             *
  1142. .*         Lbl                                    ; If While# True            *
  1143. .*                                                                            *
  1144. .*         Lbl      <expr>                        ; If While# <expr>          *
  1145. .*                  B¬<cc>.<ext>   %E%xxxx                                    *
  1146. .*         ------------------------------------------------------------------ *
  1147. .*         * The following is generated by EndW# (HERE IN THIS MACRO)         *
  1148. .*                  BRA.<ext>     Lbl                                         *
  1149. .*         %E%xxxx                                                            *
  1150. .******************************************************************************
  1151. .*
  1152.         PRINT    Push,NoMDir,NoMCall
  1153. .*
  1154.         GBLA        &StkPtr#            ; statment nesting stack pointer
  1155.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  1156.         GBLC        &LblStk#[25]        ; While# label for Cycle# and Leave#
  1157.         GBLC        &Lbl1Stk#[25]        ; label suffix for Leave# and While# 
  1158.         GBLC        &Lbl2Stk#[25]        ; extent attribute on DO
  1159. .*
  1160. .* Validate that the EndW# is nested in an While# statement
  1161. .*
  1162.     IF &StkPtr# = 0 THEN
  1163.       AERROR 'ENDW# does not end a WHILE#'
  1164.       GOTO .Exit
  1165.     ENDIF
  1166.     IF &KindStk#[&StkPtr#] ≠ 3 THEN
  1167.       AERROR 'ENDW# does not end a WHILE#'
  1168.       GOTO .Exit
  1169.     ENDIF
  1170. .*
  1171. .* Generate branch back to the top of the loop and follow it with exit label
  1172. .*
  1173.           BRA.&Lbl2Stk#[&StkPtr#] &LblStk#[&StkPtr#]
  1174. %E%&Lbl1Stk#[&StkPtr#]
  1175. .*
  1176. .* Pop the While# statement off the statement nesting stack
  1177. .*
  1178.     &StkPtr#: SETA &StkPtr#-1
  1179. .*
  1180. .Exit    PRINT    Pop
  1181.         ENDM
  1182.  
  1183.  
  1184.         TITLE    'For# - Loop control statement'
  1185.         
  1186.         MACRO
  1187. &Lbl        For#.&Sz     &Opnds,&Dreg==D0,&Opt=Y,&Clr=Y
  1188. .*
  1189. .******************************************************************************
  1190. .* For# - Loop control statement                                              *
  1191. .*                                                                            *
  1192. .* Call:   For# <op1>[=.<sz><op2>][Down]To<op3>[By<op4>][Until<exp>] DO.<ext> *
  1193. .*         EndF#                                                              *
  1194. .*                                                                            *
  1195. .* Input:  &Dreg               = D-reg to use if <op1> is not already a D-reg *
  1196. .*         &Opt                = Y[es] ==> allow DBcc optimization if possible*
  1197. .*                               N[o]  ==> Don't allow optimization at all (?)*
  1198. .*         &Clr                = Y[es] ==> Gen CLR.W if &Sz=B and DBcc ok     *
  1199. .*                               N[o]  ==> Don't CLR.W even if &Sz=B and DBcc *
  1200. .*         &StkPtr#            = statment nesting stack pointer        (SETA) *
  1201. .*                                                                            *
  1202. .* Output: &StkPtr#            = statment nesting stack ptr incremented(SETA) *
  1203. .*         &KindStk#[&StkPtr#] = statement nesting kind = 4            (SETA) *
  1204. .*         &LblStk#[&StkPtr#]  = While# label for Cycle# and Leave#    (SETC) *
  1205. .*         &Lbl1Stk#[&StkPtr#] = label suffix for Leave#               (SETC) *
  1206. .*         &Lbl2Stk#[&StkPtr#] = label suffix for Cycle#               (SETC) *
  1207. .*         &Incr#[&StkPtr#]    = 1==>TO; 0 ==>DOWNTO; -1 ==> DBcc      (SETA) *
  1208. .*         &Sz#[&StkPtr#]      = size of control reg (B, W, L)         (SETC) *
  1209. .*         &Op1#[&StkPtr#]     = <op1>, loop control counter           (SETC) *
  1210. .*         &Op3#[&StkPtr#]     = <op3>, loop limit                     (SETC) *
  1211. .*         &Op4#[&StkPtr#]     = <op4>, loop increment                 (SETC) *
  1212. .*         &Until#[&StkPtr#]   = <expr>, loop end condition            (SETC) *
  1213. .*         &Dreg#[&StkPtr#]    = work register if <op1> is not a reg   (SETC) *
  1214. .*                                                                            *
  1215. .* Code:   If a DBcc is not to be generated, there are two cases depending on *
  1216. .*         whether <op1> is a register or not:                                *
  1217. .*                                                                            *
  1218. .*         • <op1> is a D-register                                            *
  1219. .*                                                                            *
  1220. .*           * The following is generated by For# (HERE IN THIS MACRO)        *
  1221. .*                     MOVE.<sz>    <op2>,<op1>         ; Put <op2> in <op1>  *
  1222. .*                     BRA.<ext>    %L%xxxx                                   *
  1223. .*           %Loop%xxxx                                                       *
  1224. .*           ---------------------------------------------------------------  *
  1225. .*           * The following is generated by EndF#                            *
  1226. .*           %C%xxxx                                     ; Cycle# label       *
  1227. .*                    [GoTo# If# ¬<expr> Then.s %E%xxxx] ; If Until           *
  1228. .*                     ADD.<sz>     <op4>,<op1>          ; SUB if DownTo      *
  1229. .*           %L%xxxx   CMP.<sz>     <op3>,<op1>                               *
  1230. .*                     BLE          %Loopxxxx            ; BGE if DownTo      *
  1231. .*           %E%xxxx                                     ; Leave# label       *
  1232. .*                                                                            *
  1233. .*         • <op1> is NOT a D-register                                        *
  1234. .*                                                                            *
  1235. .*           * The following is generated by For# (HERE IN THIS MACRO)        *
  1236. .*                     MOVE.<sz>    <op2>,<Dreg>        ; Put <op2> in workreg*
  1237. .*                     BRA.<ext>    %L%xxxx                                   *
  1238. .*           %Loop%xxxx                                                       *
  1239. .*           ---------------------------------------------------------------  *
  1240. .*           * The following is generated by EndF#                            *
  1241. .*           %C%xxxx                                     ; Cycle# label       *
  1242. .*                    [GoTo# If# ¬<expr> Then.s %E%xxxx] ; If Until           *
  1243. .*                     MOVE.<sz>    <op1>,<Dreg>                              *
  1244. .*                     ADD.<sz>     <op4>,<Dreg>         ; SUB if DownTo      *
  1245. .*           %L%xxxx   MOVE.<sz>    <Dreg>,<op1>                              *
  1246. .*                     CMP.<sz>     <op3>,<Dreg>                              *
  1247. .*                     BLE          %Loopxxxx            ; BGE if DownTo      *
  1248. .*           %E%xxxx                                     ; Leave# label       *
  1249. .*                                                                            *
  1250. .*         If a DBcc is generated, then the For# had one of the following     *
  1251. .*         forms: For# Dn=<op2> DownTo #0 [By #1] [UNTIL <expr>] DO           *
  1252. .*                For# Dn=<op2> To     #0  By #-1 [UNTIL <expr>] DO           *
  1253. .*                For# Dn=<op2> DownTo #1 [By #1] DO                          *
  1254. .*                For# Dn=<op2> To     #1  By #-1 DO                          *
  1255. .*                                                                            *
  1256. .*           * The following is generated by For# (HERE IN THIS MACRO)        *
  1257. .*                    [CLR.W        <op1>]               ; Only if needed     *
  1258. .*                     MOVE.<sz>    <op2>,<op1>                               *
  1259. .*                    [BRA.<ext>    %C%xxxx]             ; If [Down]To #1     *
  1260. .*                    [BLT.<ext>    %E%xxxx]             ; If [Down]To #0     *
  1261. .*           %Loop%xxxx                                                       *
  1262. .*           ---------------------------------------------------------------  *
  1263. .*           * The following is generated by EndF#                            *
  1264. .*           %C%xxxx                                     ; Cycle# label       *
  1265. .*                    [GoTo# If# ¬<expr> Then.s %E%xxxx] ; Until & [Down]To #0*
  1266. .*                     DBcc         &Op1,%Loopxxxx       ; DBF if [Down]To #1 *
  1267. .*           %E%xxxx                                     ; Leave# label       *
  1268. .******************************************************************************
  1269. .*
  1270.         PRINT    Push,NoMDir,NoMCall
  1271. .*
  1272.         GBLA        &StkPtr#            ; statment nesting stack pointer
  1273.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  1274.         GBLA        &Incr#[25]        ; 1==>TO; 0 ==>DOWNTO; -1 ==> DBcc
  1275.         GBLC        &LblStk#[25]        ; For# label for Cycle# and Leave#
  1276.         GBLC        &Lbl1Stk#[25]        ; label suffix for Leave#
  1277.         GBLC        &Lbl2Stk#[25]        ; label suffix for Cycle#
  1278.         GBLC        &Sz#[25]            ; size of control reg (B, W, L)
  1279.         GBLC        &Op1#[25]            ; <op1>, loop control counter
  1280.         GBLC        &Op3#[25]            ; <op3>, loop limit
  1281.         GBLC        &Op4#[25]            ; <op4>, loop increment
  1282.         GBLC        &Until#[25]        ; <expr>, loop end  condition
  1283.         GBLC        &Dreg#[25]        ; work register if <op1> is not a reg
  1284.         GBLA        &Debug#            ; debug printing switch
  1285. .*
  1286.         LCLA        &Cp,&Sw,&First,&Last,&HaveOp2,&DBcc,&t,&b
  1287.         LCLC        &Id,&Expr,&Op2,&Siz,&Ext
  1288. .*
  1289. .* Push new statement status on the stack
  1290. .*
  1291.     IF &StkPtr# = 25 THEN
  1292.       AERROR 'Too many nested statements'
  1293.       GOTO .Exit
  1294.     ENDIF
  1295. .*
  1296.     &StkPtr#: SETA &StkPtr#+1        ; Update stack ptr
  1297.     IF &Lbl = '' THEN                ; Remember label for Until#, Cycle#, Leave#
  1298.       &LblStk#[&StkPtr#]: SETC ''
  1299.     ELSE
  1300.       &LblStk#[&StkPtr#]: SETC &Lbl
  1301. &Lbl
  1302.     ENDIF
  1303.     &Lbl1Stk#[&StkPtr#]: SETC &SysNdx    ; We always need a "leave" label
  1304.     &Lbl2Stk#[&StkPtr#]: SETC ''        ; No Cycle# yet
  1305.     &KindStk#[&StkPtr#]: SETA 4        ; Kind = 4 for For# statements
  1306. .*
  1307. .* Parse <op1>
  1308. .*
  1309.     &Cp: SETA 1                    ; Start scanning at start of &Opnds
  1310.     &Cp: SETA &Lex(&Opnds, &Cp)        ; Scan 1st token
  1311.     &Id: SETC &UC(&SysTokStr)        ; We will scan until end of <op1>
  1312.     WHILE (&Id ≠ 'TO') AND (&Id ≠ 'DOWNTO') AND (&SysToken ≠ 12) AND (&SysToken ≠ 30) DO
  1313.       &Last: SETA &Cp                ; Always assume next token is delimiter
  1314.       &Cp: SETA &Lex(&Opnds, &Cp)        ; Scan next token
  1315.       &Id: SETC &UC(&SysTokStr)
  1316.     ENDW
  1317.     IF &SysToken = 30 THEN            ; Did we crash into end of line ?
  1318.       AERROR 'Invalid loop variable'
  1319.       GOTO .Err                    ; Common error recovery
  1320.     ENDIF
  1321.     IF &SysToken = 12 THEN            ; Did we crach into "=" following <op1>
  1322.       &Last: SETA &Last-1            ; Yes, it is not part of the <op1>
  1323.     ENDIF
  1324.     &Op1#[&StkPtr#]: SETC &Trim(&Opnds[1:&Last]) ; Extract <op1>
  1325. .*
  1326. .* Determine size of the loop register.  It may be explicitly specified if we
  1327. .* have =.sz following <op1>. Whether the size is there or not, if the "=" is
  1328. .* there, scan the <op2> which follows it.
  1329. .*
  1330.     &Siz: SETC 'W'                    ; The default size is W
  1331.     IF &SysToken = 12 THEN             ; Did a "=" delimit <op1> ?
  1332.       &HaveOp2: SETA 1                ; Yes, set switch to now will have <op2>
  1333.       IF &Opnds[&Cp:1] = '.' THEN        ; Explicit .sz following "=" ?
  1334.         &Siz: SETC &UC(&Opnds[&Cp+1:1]); Yes, we will use whatever is there
  1335.         &Sz#[&StkPtr#]: SETC &Siz        ; Save for use by EndF#
  1336.         &Cp: SETA &Cp+2                ; Bump Cp over it
  1337.       ELSE
  1338.         &Sz#[&StkPtr#]: SETC 'W'        ; If no explicit size, EndF# will use W
  1339.       ENDIF
  1340.       &First: SETA &Cp                ; Prepare to scan off the <op2>
  1341.       &Cp: SETA &Lex(&Opnds, &Cp)        ; It's scan is like that for <op1>
  1342.       &Id: SETC &UC(&SysTokStr)    
  1343.       WHILE (&Id ≠ 'TO') AND (&Id ≠ 'DOWNTO') AND (&SysToken ≠ 30) DO
  1344.         &Last: SETA &Cp                ; Always assume next token is delimiter
  1345.         &Cp: SETA &Lex(&Opnds, &Cp)    ; Scan next token
  1346.         &Id: SETC &UC(&SysTokStr)
  1347.       ENDW
  1348.       IF &SysToken = 30 THEN            ; Did we crash into end of line ?
  1349.         AERROR 'TO or DOWNTO expected'
  1350.         GOTO .Err                    ; Common error recovery
  1351.       ENDIF
  1352.       &Op2: SETC &Trim(&Opnds[&First:&Last-&First+1]); Extract <op2>
  1353.     ENDIF
  1354. .*
  1355. .* At this point we should be up to the TO or DOWNTO in the For# statement
  1356. .*
  1357.     IF &Id = 'TO' THEN                ; Have TO ?
  1358.       &Incr#[&StkPtr#]: SETA 1        ; Indicate incrementing for EndF#
  1359.     ELSEIF &Id = 'DOWNTO' THEN        ; Have DOWNTO ?
  1360.       &Incr#[&StkPtr#]: SETA 0        ; Indicate decrementing for EndF#
  1361.     ELSE
  1362.       AERROR 'TO or DOWNTO expected'
  1363.       GOTO .Err
  1364.     ENDIF
  1365. .*
  1366. .* Parse <op3> following the TO or DOWNTO
  1367. .*
  1368.     &First: SETA &Cp                ; Scanned like <op1> and <op2> above
  1369.     &Cp: SETA &Lex(&Opnds, &Cp)
  1370.     &Id: SETC &UC(&SysTokStr)    
  1371.     WHILE (&Id ≠ 'UNTIL') AND (&Id ≠ 'BY') AND (&Id ≠ 'DO') AND (&SysToken ≠ 30) DO
  1372.       &Last: SETA &Cp
  1373.       &Cp: SETA &Lex(&Opnds, &Cp)
  1374.       &Id: SETC &UC(&SysTokStr)
  1375.     ENDW
  1376.     IF &SysToken = 30 THEN
  1377.       AERROR 'BY, UNTIL or DO expected'
  1378.       GOTO .Err
  1379.     ENDIF
  1380.     &Op3#[&StkPtr#]: SETC &Trim(&Opnds[&First:&Last-&First+1]); Extract <op3>
  1381. .*
  1382. .* At this point we will parse the remaining parts of the For# statement.  We
  1383. .* can have UNTIL <expr> or BY <op4> in either order. We stay in a loop to
  1384. .* process both, but allow only one occurrence of each.  The loop terminates
  1385. .* either when an error occurs or we crash into the delimiting DO. &Sw4 indicates
  1386. .* the scan status. &Sw=1 means BY was scanned. &Sw=2 or 3 means UNTIL was
  1387. .* scanned.
  1388. .*
  1389.     &Op4#[&StkPtr#]: SETC '#1'        ; Default <op4> to #1
  1390.     &Until#[&StkPtr#]: SETC ''        ; Assume there is no UNTIL clause
  1391. .*
  1392.     WHILE &Sw ≠ 4 DO                ; Loop until DO is scanned
  1393.       IF &Id = 'BY' THEN            ; Have BY clause ?
  1394.         IF (&Sw MOD 2) = 1 THEN        ; Yes, it can occur only once
  1395.           AERROR 'Only one BY clause allowed'
  1396.           GOTO .Err
  1397.         ENDIF
  1398.         &First: SETA &Cp            ; Scan over the <op4> the usual way
  1399.         &Cp: SETA &Lex(&Opnds, &Cp)
  1400.         &Id: SETC &UC(&SysTokStr)
  1401.         WHILE (&Id ≠ 'DO') AND (&Id ≠ 'UNTIL') AND (&SysToken ≠ 30) DO
  1402.           &Last: SETA &Cp
  1403.          &Cp: SETA &Lex(&Opnds, &Cp)
  1404.          &Id: SETC &UC(&SysTokStr)
  1405.         ENDW
  1406.         IF &SysToken ≠ 30 THEN
  1407.           &Op4#[&StkPtr#]: SETC &Trim(&Opnds[&First:&Last-&First+1]); Extract <op4>
  1408.         ENDIF
  1409.         &Sw: SETA &Sw+1                ; Indicate that BY has been scanned
  1410.       ELSEIF &Id = 'UNTIL' THEN        ; Have UNTIL clause ?
  1411.         IF (&Sw DIV 2) = 1 THEN        ; Yes, it can occur only once
  1412.           AERROR 'Only one UNTIL clause allowed'
  1413.           GOTO .Err
  1414.         ENDIF
  1415.         &First: SETA &Cp            ; Scan over the <expr> the usual way
  1416.         &Cp: SETA &Lex(&Opnds, &Cp)
  1417.         &Id: SETC &UC(&SysTokStr)
  1418.         WHILE (&Id ≠ 'DO') AND (&Id ≠ 'BY') AND (&SysToken ≠ 30) DO
  1419.           &Last: SETA &Cp
  1420.          &Cp: SETA &Lex(&Opnds, &Cp)
  1421.          &Id: SETC &UC(&SysTokStr)
  1422.         ENDW
  1423.         IF &SysToken ≠ 30 THEN
  1424.           &Until#[&StkPtr#]: SETC &Trim(&Opnds[&First:&Last-&First+1]); Extract <expr>
  1425.         ENDIF
  1426.         &Sw: SETA &Sw+2                ; Indicate that UNTIL has been scanned
  1427.       ELSEIF &Id = 'DO' THEN            ; Have delimiting DO ?
  1428.         &Sw: SETA 4 ; LEAVE !!        ; Yes, set switch to stop loop
  1429.         IF &Opnds[&Cp:1] = '.' THEN    ; Have explicit <extent> on the DO ?
  1430.          &Ext: SETC &UC(&Trim(&Opnds[&Cp+1:1])); Yes, use it
  1431.         ENDIF
  1432.         &Ext: SETC &Default(&Ext, 'W')    ; Make sure of the <extent>
  1433.       ELSE
  1434.         AERROR 'BY, UNTIL or DO expected'
  1435.         GOTO .Err
  1436.       ENDIF
  1437.     ENDW
  1438. .*
  1439. .* At this point we're done with the scan.  Wasn't that fun?  Now that we have
  1440. .* all the stuff, it's time to use it.  First, we see if we need the work
  1441. .* register.  We do if <op1> is not a D-register.
  1442. .*
  1443.     IF &SubStr(&Type(&Op1#[&StkPtr#]), 1, 5) ≠ 'REG D' THEN
  1444.       &Dreg#[&StkPtr#]: SETC &Dreg    ; Indicate we are using a work reg    
  1445.     ELSE
  1446.       &Dreg#[&StkPtr#]: SETC ''        ; If work reg not needed, set to <null>
  1447.     ENDIF
  1448.     GOTO .Opt                        ; Hop over error recovery code
  1449. .*
  1450. .* Once the stack is set up, and an error occurs, it seems more appropriate to
  1451. .* "force" the For# into a legal state rather than dropping it on the floor. We
  1452. .* do this by pretending we scanned For# D0=#0 To #0 Do.
  1453. .*
  1454. .Err    ANOP
  1455.     &Op1#[&StkPtr#]:   SETC 'D0'        ; Set <op1> to D0
  1456.     &HaveOp2:          SETA 1        ; Have "="
  1457.     &Op3#[&StkPtr#]:   SETC '#0'        ; Set <op3> to #0
  1458.     &Op4#[&StkPtr#]:   SETC '#1'        ; Set <op4> to #1
  1459.     &Until#[&StkPtr#]: SETC ''        ; No UNTIL
  1460.     &Incr#[&StkPtr#]:  SETA 1        ; Incrementing
  1461.     &Dreg#[&StkPtr#]:  SETC ''        ; No work reg needed
  1462.     &Ext:              SETC 'W'        ; An <extent> of W
  1463. .*
  1464. .* See if we can optimize the code to use a DBcc.  We can if we have either of
  1465. .* the four following For# statements (and &Opt=Yes):
  1466. .*
  1467. .*  • For# Dn=<op2> DownTo #0 [By #1] [Until <expr>] DO
  1468. .*  • For# Dn=<op2> To     #0  By #-1 [Until <expr>] DO
  1469. .*  • For# Dn=<op2> DownTo #1 [By #1] DO
  1470. .*  • For# Dn=<op2> To     #1  By #-1 DO
  1471. .*
  1472. .* The switch &DBcc is set to 1 if we have any of these loops.  In order to tell
  1473. .* EndF# what's going on, and in order to avoid another global array, we will
  1474. .* set the to/downto indicator, &Incr#, to -1 for these cases.
  1475. .*
  1476. .Opt    IF &UC(&Opt[1:1]) = 'Y' THEN                    ; Optimization allowed ?
  1477.       IF &Dreg#[&StkPtr#] = '' THEN                ; <op1> is a Dn
  1478.         IF &Siz ≠ 'L' THEN                           ; Word, byte sized reg
  1479.          IF &Op3#[&StkPtr#,1:1] = '#' THEN         ; <op3> is #n (Down[To])
  1480.            &t: SETA &Eval(&Op3#[&StkPtr#,2:255])
  1481.            IF &t = 0 THEN                        ; Down[To] #0
  1482.             IF &Op4#[&StkPtr#,1:1] = '#' THEN        ; <op4> is #m (By)
  1483.               &b: SETA &Eval(&Op4#[&StkPtr#,2:255])
  1484.               IF &b = 1 THEN                    ; By #1
  1485.                 IF NOT &Incr#[&StkPtr#] THEN
  1486.                  &DBcc: SETA 1                    ; DownTo #0 By #1
  1487.                 ENDIF
  1488.               ELSEIF &b = -1 THEN                ; By #-1
  1489.                 IF &Incr#[&StkPtr#] THEN
  1490.                  &DBcc: SETA 1                    ; To #0 By -1
  1491.                 ENDIF
  1492.               ENDIF
  1493.             ENDIF
  1494.            ELSEIF (&t=1)AND(&Until#[&StkPtr#]='')THEN; Down[To] #1 (no UNTIL)
  1495.             IF &Op4#[&StkPtr#,1:1] = '#' THEN        ; <op4> is #m (By)
  1496.               &b: SETA &Eval(&Op4#[&StkPtr#,2:255])
  1497.               IF &b = 1 THEN                    ; By #1
  1498.                 IF NOT &Incr#[&StkPtr#] THEN
  1499.                  &DBcc: SETA 1                    ; DownTo #1 By #1
  1500.                 ENDIF
  1501.               ELSEIF &b = -1 THEN                ; By #-1
  1502.                 IF &Incr#[&StkPtr#] THEN
  1503.                  &DBcc: SETA 1                    ; To #1 By -1
  1504.                 ENDIF
  1505.               ENDIF
  1506.             ENDIF
  1507.            ENDIF
  1508.          ENDIF
  1509.         ENDIF
  1510.       ENDIF
  1511.     ENDIF
  1512. .*
  1513. .* If we are generating a DBcc loop, gen code to not enter the loop if the
  1514. .* loop counter value is negative.  If we are not generating a DBcc loop, gen
  1515. .* code to branch to the end of the loop, the EndF# code, where all the actual
  1516. .* work is done.
  1517. .*
  1518.     IF &DBcc THEN
  1519.       &Incr#[&StkPtr#]: SETA -1
  1520.       IF &HaveOp2 THEN                ; If explicit initial setting, do it
  1521.         IF (&Siz = 'B') AND (&UC(&Clr[1:1]) = 'Y') THEN; Clear the reg 1st ?
  1522.           IF &Op2[1:1] = '#' THEN        ; Yes, but only if <op2> is not a const
  1523.            &Siz: SETC 'W'            ; If const, init reg as a word
  1524.          ELSE
  1525.           CLR.W     &Op1#[&StkPtr#]
  1526.          ENDIF
  1527.         ENDIF
  1528.           MOVE.&Siz &Op2,&Op1#[&StkPtr#]
  1529.       ENDIF
  1530.       IF &t = 0 THEN                ; ... [Down]To #0 By #1 ...
  1531.           BLT.&Ext  %E%&SysNdx
  1532.       ELSE                        ; ... [Down]To #1 By #1 ...
  1533.         &Lbl2Stk#[&StkPtr#]: SETC &SysNdx; Use the Cycle# label as branch dst
  1534.           BRA.&Ext  %C%&SysNdx
  1535.       ENDIF
  1536. %Loop%&SysNdx
  1537.     ELSE
  1538.       IF &HaveOp2 THEN                ; If explicit initial setting, do it
  1539.         IF &Dreg#[&StkPtr#] ≠ '' THEN
  1540.           MOVE.&Siz &Op2,&Dreg#[&StkPtr#]
  1541.         ELSE
  1542.           MOVE.&Siz &Op2,&Op1#[&StkPtr#]
  1543.         ENDIF
  1544.       ENDIF
  1545.           BRA.&Ext  %L%&SysNdx
  1546. %Loop%&SysNdx
  1547.     ENDIF
  1548. .*
  1549. .* The following is used to verify our scan!
  1550. .*
  1551.     IF &Debug# THEN
  1552.       Write 'FOR#', ' ', &Op1#[&StkPtr#]
  1553.       IF &HaveOp2 THEN
  1554.         Write '=.', &Sz#[&StkPtr#], ' ', &Op2
  1555.       ENDIF
  1556.       IF &Incr#[&StkPtr#] THEN
  1557.         Write ' TO '
  1558.       ELSE
  1559.         Write ' DOWNTO '
  1560.       ENDIF
  1561.       Write    &Op3#[&StkPtr#], ' BY ', &Op4#[&StkPtr#]
  1562.       IF &Until#[&StkPtr#] ≠ '' THEN
  1563.         Write ' UNTIL "', &Until#[&StkPtr#], '"'
  1564.       ENDIF
  1565.       WriteLn    ' DO.', &Ext
  1566.     ENDIF
  1567. .*
  1568. .Exit     PRINT     Pop
  1569.         ENDM
  1570.  
  1571.  
  1572.         TITLE    'EndF# - End of For# statement'
  1573.         
  1574.         MACRO
  1575.         EndF#
  1576. .*
  1577. .******************************************************************************
  1578. .* EndF# - End of For# statement                                              *
  1579. .*                                                                            *
  1580. .* Call:   For# <op1>[=.<sz><op2>][Down]To<op3>[By<op4>][Until<exp>] DO.<ext> *
  1581. .*         EndF#                                                              *
  1582. .*                                                                            *
  1583. .* Input:  &StkPtr#            = statment nesting stack ptr incremented(SETA) *
  1584. .*         &KindStk#[&StkPtr#] = statement nesting kind = 4            (SETA) *
  1585. .*         &LblStk#[&StkPtr#]  = While# label for Cycle# and Leave#    (SETC) *
  1586. .*         &Lbl1Stk#[&StkPtr#] = label suffix for Leave#               (SETC) *
  1587. .*         &Lbl2Stk#[&StkPtr#] = label suffix for Cycle#               (SETC) *
  1588. .*         &Incr#[&StkPtr#]    = 1==>TO; 0 ==>DOWNTO; -1 ==> DBcc      (SETA) *
  1589. .*         &Sz#[&StkPtr#]      = size of control reg (B, W, L)         (SETC) *
  1590. .*         &Op1#[&StkPtr#]     = <op1>, loop control counter           (SETC) *
  1591. .*         &Op3#[&StkPtr#]     = <op3>, loop limit                     (SETC) *
  1592. .*         &Op4#[&StkPtr#]     = <op4>, loop increment                 (SETC) *
  1593. .*         &Until#[&StkPtr#]   = <expr>, loop end condition            (SETC) *
  1594. .*         &Dreg#[&StkPtr#]    = work register if <op1> is not a reg   (SETC) *
  1595. .*                                                                            *
  1596. .* Output: &StkPtr#            = statment nesting stack ptr decremented(SETA) *
  1597. .*                                                                            *
  1598. .* Code:   If a DBcc is not to be generated, there are two cases depending on *
  1599. .*         whether <op1> is a register or not:                                *
  1600. .*                                                                            *
  1601. .*         • <op1> is a D-register                                            *
  1602. .*                                                                            *
  1603. .*           * The following is generated by For#                             *
  1604. .*                     MOVE.<sz>    <op2>,<op1>         ; Put <op2> in <op1>  *
  1605. .*                     BRA.<ext>    %L%xxxx                                   *
  1606. .*           %Loop%xxxx                                                       *
  1607. .*           ---------------------------------------------------------------  *
  1608. .*           * The following is generated by EndF# (HERE IN THIS MACRO)       *
  1609. .*           %C%xxxx                                     ; Cycle# label       *
  1610. .*                    [GoTo# If# ¬<expr> Then.s %E%xxxx] ; If Until           *
  1611. .*                     ADD.<sz>     <op4>,<op1>          ; SUB if DownTo      *
  1612. .*           %L%xxxx   CMP.<sz>     <op3>,<op1>                               *
  1613. .*                     BLE          %Loopxxxx            ; BGE if DownTo      *
  1614. .*           %E%xxxx                                     ; Leave# label       *
  1615. .*                                                                            *
  1616. .*         • <op1> is NOT a D-register                                        *
  1617. .*                                                                            *
  1618. .*           * The following is generated by For#                             *
  1619. .*                     MOVE.<sz>    <op2>,<Dreg>        ; Put <op2> in workreg*
  1620. .*                     BRA.<ext>    %L%xxxx                                   *
  1621. .*           %Loop%xxxx                                                       *
  1622. .*           ---------------------------------------------------------------  *
  1623. .*           * The following is generated by EndF# (HERE IN THIS MACRO)       *
  1624. .*           %C%xxxx                                     ; Cycle# label       *
  1625. .*                    [GoTo# If# ¬<expr> Then.s %E%xxxx] ; If Until           *
  1626. .*                     MOVE.<sz>    <op1>,<Dreg>                              *
  1627. .*                     ADD.<sz>     <op4>,<Dreg>         ; SUB if DownTo      *
  1628. .*           %L%xxxx   MOVE.<sz>    <Dreg>,<op1>                              *
  1629. .*                     CMP.<sz>     <op3>,<Dreg>                              *
  1630. .*                     BLE          %Loopxxxx            ; BGE if DownTo      *
  1631. .*           %E%xxxx                                     ; Leave# label       *
  1632. .*                                                                            *
  1633. .*         If a DBcc is generated, then the For# had one of the following     *
  1634. .*         forms: For# Dn=<op2> DownTo #0 [By #1] [UNTIL <expr>] DO           *
  1635. .*                For# Dn=<op2> To     #0  By #-1 [UNTIL <expr>] DO           *
  1636. .*                For# Dn=<op2> DownTo #1 [By #1] DO                          *
  1637. .*                For# Dn=<op2> To     #1  By #-1 DO                          *
  1638. .*                                                                            *
  1639. .*           * The following is generated by For#                             *
  1640. .*                    [CLR.W        <op1>]               ; Only if needed     *
  1641. .*                     MOVE.<sz>    <op2>,<op1>                               *
  1642. .*                    [BRA.<ext>    %C%xxxx]             ; If [Down]To #1     *
  1643. .*                    [BLT.<ext>    %E%xxxx]             ; If [Down]To #0     *
  1644. .*           %Loop%xxxx                                                       *
  1645. .*           ---------------------------------------------------------------  *
  1646. .*           * The following is generated by EndF# (HERE IN THIS MACRO)       *
  1647. .*           %C%xxxx                                     ; Cycle# label       *
  1648. .*                    [GoTo# If# ¬<expr> Then.s %E%xxxx] ; Until & [Down]To #0*
  1649. .*                     DBcc         &Op1,%Loopxxxx       ; DBF if [Down]To #1 *
  1650. .*           %E%xxxx                                     ; Leave# label       *
  1651. .******************************************************************************
  1652. .*
  1653.         PRINT    Push,NoMDir,NoMCall
  1654. .*
  1655.         GBLA        &StkPtr#            ; statment nesting stack pointer
  1656.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  1657.         GBLA        &Incr#[25]        ; 1==>TO; 0 ==>DOWNTO; -1 ==> DBcc
  1658.         GBLC        &LblStk#[25]        ; For# label for Cycle# and Leave#
  1659.         GBLC        &Lbl1Stk#[25]        ; label suffix for Leave#
  1660.         GBLC        &Lbl2Stk#[25]        ; label suffix for Cycle#
  1661.         GBLC        &Sz#[25]            ; size of control reg (B, W, L)
  1662.         GBLC        &Op1#[25]            ; <op1>, loop control counter
  1663.         GBLC        &Op3#[25]            ; <op3>, loop limit
  1664.         GBLC        &Op4#[25]            ; <op4>, loop increment
  1665.         GBLC        &Until#[25]        ; <expr>, loop end  condition
  1666.         GBLC        &Dreg#[25]        ; work register if <op1> is not a reg
  1667.         GBLC        &CC#                ; Condition code table
  1668.         GBLA        &FalseUsed#        ; <ea> AND(OR) <ea> with &JumpCond=1(0)
  1669. .*
  1670.         LCLC        &Op1,&Op3,&OP4,&Lbl,&Sz,&DBcc,&Dreg
  1671.         LCLA        &Incr
  1672. .*
  1673. .* Validate that the EndF# is nested in an For# statement
  1674. .*
  1675.     IF &StkPtr# = 0 THEN
  1676.       AERROR 'ENDF# does not end a FOR#'
  1677.       GOTO .Exit
  1678.     ENDIF
  1679.     IF &KindStk#[&StkPtr#] ≠ 4 THEN
  1680.       AERROR 'ENDW# does not end a FOR#'
  1681.       GOTO .Exit
  1682.     ENDIF
  1683. .*
  1684. .* Copy the subscripted globals into non-subscripted locals for efficiency
  1685. .*
  1686.     &Op1: SETC &Op1#[&StkPtr#]        ; <op1>
  1687.     &Op3: SETC &Op3#[&StkPtr#]        ; <op3>
  1688.     &Op4: SETC &Op4#[&StkPtr#]        ; <op4>
  1689.     &Lbl: SETC &Lbl1Stk#[&StkPtr#]    ; Top of loop label suffix
  1690.     &Sz:  SETC &Sz#[&StkPtr#]        ; Size of loop register
  1691.     &Incr:SETA &Incr#[&StkPtr#]        ; Loop direction
  1692.     IF &Incr < 0 THEN                ; Are we to do a DBcc ?
  1693.       &DBcc: SETC 'F'                ; Yes, assume it will be a DBF
  1694.     ENDIF
  1695.     &Dreg: SETC &Dreg#[&StkPtr#]        ; Set work reg, if any
  1696. .*
  1697. .* If there was a Cycle# statement, define the Cycle# label prior loop code
  1698. .*
  1699.     IF &Lbl2Stk#[&StkPtr#] ≠ '' THEN
  1700. %C%&Lbl2Stk#[&StkPtr#]
  1701.     ENDIF
  1702. .*
  1703. .* If there is an UNTIL clause generate code to exit loop if UNTIL's <expr> is
  1704. .* false.  But, if we are generated a DBcc loop, and the <expr> is just a simple
  1705. .* condition code, we can combine the condition code test with the DBcc.
  1706. .*
  1707.     IF &Until#[&StkPtr#] ≠ '' THEN    ; Have an UNTIL clause ?
  1708.       &FalseUsed#: SETA 0            ; Make sure of switch
  1709.       IF &DBcc = '' THEN            ; Yes, are we generation a DBcc loop ?
  1710.         Expr#.S &Until#[&StkPtr#],True=%E%&Lbl,False=%F%&SysNdx,JumpCond=0; No, treat like If#
  1711.       ELSE                        ; If DBcc loop
  1712.         &DBcc: SETC &Until#[&StkPtr#]    ; See if <expr> is a simple condition code
  1713.         &CC#: SETC 'EQ.NE.LE.LT.GE.GT.MI.PL.HI.LS.LO.CC.CS.NZ.HS.VC.VS.'
  1714.         IF &Pos(&Concat(&UC(&DBcc), '.'), &CC#) = 0 THEN; Not simple condition code
  1715.          Expr#.S &DBcc,True=%E%&Lbl,False=%F%&SysNdx,JumpCond=0; Still treat like an If#
  1716.          &DBcc: SETC 'F'            ; And loop will become a DBF
  1717.         ENDIF
  1718.       ENDIF
  1719.       IF &FalseUsed# THEN            ; If we need False label, generate it
  1720. %F%&SysNdx
  1721.       ENDIF
  1722.     ENDIF
  1723. .*
  1724. .* Gen end of loop code for all cases:
  1725. .*   • Case 1 - TO loop, not DBcc, needing work reg
  1726. .*   • Case 2 - TO loop, not DBcc, <op1> is a D-register
  1727. .*   • Case 3 - DOWNTO loop, not DBcc, needing work reg
  1728. .*   • Case 4 - DOWNTO loop, not DBcc, <op1> is a D-register
  1729. .*   • Case 5 - DBcc loop
  1730. .*
  1731.     PRINT Push,NoWarn                ; Suppress any Assembler branch warnings
  1732.     IF &Incr = 1 THEN                ; TO loops
  1733.       IF &Dreg ≠ '' THEN            ; Case 1 - TO loop, not DBcc, needing work reg
  1734.           MOVE.&Sz  &Op1,&Dreg
  1735.           ADD.&Sz   &Op4,&Dreg
  1736. %L%&Lbl   MOVE.&Sz  &Dreg,&Op1
  1737.           CMP.&Sz   &Op3,&Dreg
  1738.           BLE       %Loop%&Lbl
  1739. %E%&Lbl
  1740.       ELSE                        ; Case 2 - TO loop, not DBcc, <op1> is a D-reg
  1741.           ADD.&Sz   &Op4,&Op1
  1742. %L%&Lbl   CMP.&Sz   &Op3,&Op1
  1743.           BLE       %Loop%&Lbl
  1744. %E%&Lbl
  1745.       ENDIF
  1746.     ELSEIF &Incr = 0 THEN            ; DOWNTO loops
  1747.       IF &Dreg ≠ '' THEN            ; Case 3 - DOWNTO loop, not DBcc, needing work reg
  1748.           MOVE.&Sz  &Op1,&Dreg
  1749.           SUB.&Sz   &Op4,&Dreg
  1750. %L%&Lbl   MOVE.&Sz  &Dreg,&Op1
  1751.           CMP.&Sz   &Op3,&Dreg
  1752.           BGE       %Loop%&Lbl
  1753. %E%&Lbl
  1754.       ELSE                        ; Case 4 - DOWNTO loop, not DBcc, <op1> is a D-reg
  1755.           SUB.&Sz   &Op4,&Op1
  1756. %L%&Lbl   CMP.&Sz   &Op3,&Op1
  1757.           BGE       %Loop%&Lbl
  1758. %E%&Lbl
  1759.       ENDIF
  1760.     ELSE                            ; Case 5 - DBcc loop
  1761.           DB&DBcc       &Op1,%Loop%&Lbl
  1762. %E%&Lbl
  1763.     ENDIF
  1764.     PRINT Pop
  1765. .*
  1766. .* Pop the For# statement off the statement nesting stack
  1767. .*
  1768.     &StkPtr#: SETA &StkPtr#-1
  1769. .*
  1770. .Exit    PRINT    Pop
  1771.         ENDM
  1772.  
  1773.         
  1774.         TITLE    'Switch# - Multi-way decision'
  1775.         
  1776.         MACRO
  1777. &Lbl        Switch#.&Sz &Selector,&Dreg==D0,&JmpTbl==N,&ChkRng==N
  1778. .*
  1779. .******************************************************************************
  1780. .* Switch# - Multi-way decision                                               *
  1781. .*                                                                            *
  1782. .* Call:   Switch#.<sz> <selector>,Dreg=Dn,JmpTbl={<ext>|Y|N},ChkRng={Y|N}*** *
  1783. .*         Case#.<ext> <ae1>[..<ae2>],...                                     *
  1784. .*         Default#                                                           *
  1785. .*         EndS#                                                              *
  1786. .*                                                                            *
  1787. .* Input:  &Selector           = <ea> used to specify the case selector       *
  1788. .*         &Dreg               = D-register to used to detemine which case    *
  1789. .*         &JmpTbl             = <ext> ==> determine case with a jump table   *
  1790. .*                                         <ext> is branch extent to EndS#    *
  1791. .*                                         Note, Y[es] same as <ext>=W        *
  1792. .*                             = N[o]  ==> determine case by repeated SUBs    *
  1793. .*         &ChkRng             = Y[es] ==> range check a jump table selector  *
  1794. .*                             = N[o]  ==> assume selector covered by all the *
  1795. .*                                         cases                              *
  1796. .*                                                                            *
  1797. .*         &StkPtr#            = statment nesting stack pointer        (SETA) *
  1798. .*                                                                            *
  1799. .* Output: &StkPtr#            = statment nesting stack ptr incremented(SETC) *
  1800. .*         &KindStk#[&StkPtr#] = statement nesting kind = 5            (SETA) *
  1801. .*         &LblStk#[&StkPtr#]  = Switch# label for Leave#              (SETC) *
  1802. .*         &Lbl1Stk#[&StkPtr#] = most recent Case# <ae> value (<null>) (SETC) *
  1803. .*         &Lbl2Stk#[&StkPtr#] = next Case# label suffix (<null>)      (SETC) *
  1804. .*         &CaseStkPtr#        = case stack pointer for nested switches(SETA) *
  1805. .*         &FrstCase#[&StkPtr#]= index of 1st Case# label in &CaseStk# (SETA) *
  1806. .*         &Low#[&StkPtr#]     = lowest case value (init to $7FFFFFFF) (SETA) *
  1807. .*         &High#[&StkPtr#]    = highest case value (init to $80000000)(SETA) *
  1808. .*         &JmpTbl#[&StkPtr#]  = 0==>SUB's; 1==>JumpTbl; 2==>ChkRng=Y  (SETA) *
  1809. .*         &EndSwLbl#[&StkPtr#]= end case label suffix for Leave#      (SETC) *
  1810. .*         &Dreg#[&StkPtr#]    = D-register used by this Switch#       (SETC) *
  1811. .*         &Default#[&StkPtr#] = default label from Default# (<null>)  (SETC) *
  1812. .*                                                                            *
  1813. .* Code:   There are two cases depending on whether a jump table is being     *
  1814. .*         used or not:                                                       *
  1815. .*                                                                            *
  1816. .*         • JumpTbl = <ext> | Yes (<ext> = W)                                *
  1817. .*                                                                            *
  1818. .*           * The following is generated by Switch# (HERE IN THIS MACRO)     *
  1819. .*                    MOVEQ      #0,<Dreg>           ; If <sz> is B           *
  1820. .*                    MOVE.<sz>  <Selector>,<Dreg>                            *
  1821. .*                    BRA.<ext>  %L%xxxx                                      *
  1822. .*           ---------------------------------------------------------------- *
  1823. .*           * The following is generated by Case#                            *
  1824. .*           %C%xxxx                                                          *
  1825. .*           ---------------------------------------------------------------- *
  1826. .*           * The following is generated by Default#                         *
  1827. .*           %D%xxxx                                                          *
  1828. .*           ---------------------------------------------------------------- *
  1829. .*           * The following is generated by EndS#                            *
  1830. .*           %L%xxxx                                                          *
  1831. .*                  [ CMPI.W     #Low,<Dreg>  ]      ; These 4 lines gened if *
  1832. .*                  [ BLT        %D%xxxx      ]      ; ChkRng=Yes on Switch#  *
  1833. .*                  [ CMPI.W     #High,<Dreg> ]      ; Low/High are the lowest*
  1834. .*                  [ BGT        %D%xxxx      ]      ; and highest Case# tags *
  1835. .*                    ADD.W      <Dreg>,<Dreg>       ; Get jump table entry   *
  1836. .*                    MOVE.W     %T%xxxx-(2*Low)(<Dreg>.W),<Dreg>             *
  1837. .*                    JMP        %T%xxxx(<Dreg>.W)   ; Jump to proper Case#   *
  1838. .*           %T%xxxx                                 ; The jump table         *
  1839. .*                    DCB.W      &High-&Low+1,%D%xxxx-%T%xxxx; Default holes  *
  1840. .*                    ORG        %T%xxxx+2*(<ae>-Low) ; Case# <ae>            *
  1841. .*                    DC.W       %C%xxxx-%T%xxxx                              *
  1842. .*                    - - -                                                   *
  1843. .*                    ORG        %T%xxxx+2*(<ae1>-Low); Case# <ae1>..<ae2>    *
  1844. .*                    DCB.W      <n>,%C%xxxx-%T%xxxx  ; <n> = <ae2>-<ae1>+1   *
  1845. .*                    - - -                                                   *
  1846. .*                    ORG                            ; Reset PC to end of tbl *
  1847. .*           %E%xxxx                                 ; Used by Leave# and as  *
  1848. .*                                                     default if no Default# *
  1849. .*                                                                            *
  1850. .*         • JumpTbl = No                                                     *
  1851. .*                                                                            *
  1852. .*           * The following is generated by Switch# (HERE IN THIS MACRO)     *
  1853. .*                    MOVEQ      #0,<Dreg>           ; If <sz> is B           *
  1854. .*                    MOVE.<sz>  <Selector>,<Dreg>                            *
  1855. .*           ---------------------------------------------------------------- *
  1856. .*           * The following is generated by Case#                            *
  1857. .*           %C%xxxx                                 ; Not on 1st Case#       *
  1858. .*                    -------------------------------; Not last Case# <ae>    *
  1859. .*                    SUB        #<ae>-<ae'>,<Dreg>  ; Case# <ae>,...         *
  1860. .*                    BEQ.S      %T%xxxx                                      *
  1861. .*                    -------------------------------; Last Case# tag range   *
  1862. .*                    SUB        #<ae1>-<ae>,<Dreg>  ; Case# ...,<ae1>..<ae2> *
  1863. .*                    BLT.<ext>  %C%xxx1             ; Next %C%xxxx Case# lbl *
  1864. .*                    SUB        #<ae2>-<ae1>,<Dreg>                          *
  1865. .*                    BGT.<ext>  %C%xxx1                                      *
  1866. .*                    ===============================; Not last Case# tag rng *
  1867. .*                    SUB        #<ae1>-<ae'>,<Dreg> ; Case# <ae1>..<ae2>,... *
  1868. .*                    BLT.S      @x                                           *
  1869. .*                    SUB        #<ae2>-<ae1>,<Dreg>                          *
  1870. .*                    BLE.S      %T%xxxx                                      *
  1871. .*                    -------------------------------; Last Case# <ae>        *
  1872. .*           @x       SUB        #<ae>-<ae2>,<Dreg>  ; Case# ...,<ae>         *
  1873. .*                    BNE.<ext>  %C%xxx1                                      *
  1874. .*           %T%xxxx                                                          *
  1875. .*           ---------------------------------------------------------------- *
  1876. .*           * The following is generated by Default#                         *
  1877. .*           %D%xxxx                                                          *
  1878. .*           ---------------------------------------------------------------- *
  1879. .*           * The following is generated by EndS#                            *
  1880. .*           %C%xxx1 [EQU         %D%xxxx]           ; EQU if no Default#     *
  1881. .*           %E%xxxx                                 ; Used by Leave#         *
  1882. .******************************************************************************
  1883. .*
  1884.         PRINT    Push,NoMDir,NoMCall
  1885. .*
  1886.         GBLA        &StkPtr#            ; statment nesting stack pointer
  1887.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  1888.         GBLC        &LblStk#[25]        ; Switch# label for Leave#
  1889.         GBLC        &Lbl1Stk#[25]        ; most recent Case# <ae> value
  1890.         GBLC        &Lbl2Stk#[25]        ; next Case# label suffix
  1891.         GBLA        &CaseStkPtr#        ; case stack pointer for nested switches
  1892.         GBLA        &FrstCase#[25]        ; index of 1st Case# label in &CaseStk#
  1893.         GBLA        &Low#[25]            ; lowest case value
  1894.         GBLA        &High#[25]        ; highest case value
  1895.         GBLA        &JmpTbl#[25]        ; 0==>SUB's; 1==>JumpTbl; 2==>ChkRng=Y
  1896.         GBLC        &EndSwLbl#[25]        ; end case label suffix for Leave#
  1897.         GBLC        &Dreg#[25]        ; D-register used by this Switch#
  1898.         GBLC        &Default#[25]        ; default label from Default#
  1899. .*
  1900.         LCLC        &t1,&t2
  1901. .*
  1902. .* Push new statement status on the stack
  1903. .*
  1904.     IF &StkPtr# = 25 THEN
  1905.       AERROR 'Too many nested statements'
  1906.       GOTO .Exit
  1907.     ENDIF
  1908. .*
  1909.     &StkPtr#: SETA &StkPtr#+1        ; Update stack ptr
  1910.     &KindStk#[&StkPtr#]: SETA 5        ; Kind = 5 for Switch# statements    
  1911.     &Default#[&StkPtr#]: SETC ''        ; There is no default label yet
  1912.     &EndSwLbl#[&StkPtr#]: SETC &SysNdx    ; Set end of case label suffix
  1913.     IF &Lbl = '' THEN                ; Remember label for Leave#
  1914.       &LblStk#[&StkPtr#]: SETC ''
  1915.     ELSE
  1916.       &LblStk#[&StkPtr#]: SETC &Lbl
  1917. &Lbl
  1918.     ENDIF
  1919. .*
  1920. .* Generate code to copy <selector> into the D-reg if the selector is not
  1921. .* already the specified D-register.
  1922. .*
  1923.     &t1: SETC &Type(&Selector)        ; Check out the selector
  1924.     IF (&t1[1:5] ≠ 'REG D') OR (&t1[5:2] ≠ &UC(&Dreg)) THEN
  1925.       IF &UC(&Sz) = 'B' THEN
  1926.           MOVEQ     #0,&Dreg
  1927.       ENDIF
  1928.           MOVE.&Sz  &Selector,&Dreg
  1929.     ENDIF
  1930.     &Dreg#[&StkPtr#]: SETC &Dreg        ; Remember the D-reg we are using
  1931. .*
  1932. .* Now we split the processing up according to whether we are going to use a
  1933. .* jump table or not.
  1934. .*  • JumpTbl = Yes  Initialize high/low values and gen branch to EndS# code.
  1935. .*                   Set &FrstCase#[&StkPtr#] to the index of the &CaseStk#
  1936. .*                   (case label stack) entry to hold the first Case# label.
  1937. .*                   &CaseStk# is a stack use to hold a Switch#'s case labels.
  1938. .*                   &FrstCase#[&StkPtr#] points to the 1st label and the stack's
  1939. .*                   stack pointer, &CaseStkPtr#, points to the last label.
  1940. .*  • JumpTbl = No   Not much to do here! All that need be done is to init the
  1941. .*                   previous Case# value to <null> so that Case# knows when it
  1942. .*                   is doing the 1st SUBtract in a set of repeated subtractions.
  1943. .*
  1944.     &t1: SETC &UC(&JmpTbl[1:1])        ; See if we are to us a jump table
  1945.     IF &Pos(&t1, 'YWSBL') THEN        ; JumpTbl = Y[es] | S | B | W | L
  1946.       IF &t1 = 'Y' THEN                ; If we had JmpTbl = Y[es]...
  1947.         &t1: SETC 'W'                ; ...pretend it was JmpTbl = W
  1948.       ENDIF
  1949.       &t2: SETC &UC(&ChkRng[1:1])        ; Should a range check be done on selector
  1950.       IF &t2 = 'Y' THEN                ; ChkRng = Y[es]
  1951.         &JmpTbl#[&StkPtr#]: SETA 2    ; Tell EndS# to gen range check code
  1952.       ELSE                        ; ChkRng = N[o]
  1953.         IF &t2 ≠ 'N' THEN
  1954.          AERROR 'Invalid ChkRng specification -- ChkRng=N assumed'        
  1955.         ENDIF
  1956.         &JmpTbl#[&StkPtr#]: SETA 1    ; Tell EndS# we're only doing jump table
  1957.       ENDIF
  1958.       &Low#[&StkPtr#]:  SETA $7FFFFFFF    ; Init lowest case tag value
  1959.       &High#[&StkPtr#]: SETA $80000000    ; Init highest case tag value
  1960.       PRINT Push,NoWarn                ; Gen branch to EndS# code
  1961.           BRA.&t1   %L%&SysNdx
  1962.       PRINT Pop
  1963.       IF &CaseStkPtr# = 250 THEN        ; Prepare to set &FrstCase#[&StkPtr#]
  1964.         AERROR 'Too many "JmpTbl" cases -- max of 250 allowed'
  1965.       ELSE
  1966.         &FrstCase#[&StkPtr#]: SETA &CaseStkPtr#+1; 1st Case# label index
  1967.       ENDIF
  1968.     ELSE                            ; JumpTbl = N[o]
  1969.       &JmpTbl#[&StkPtr#]:  SETA 0        ; Tell EndS# we're not using a jump table
  1970.       &Lbl1Stk#[&StkPtr#]: SETC ''    ; There is no previous  case yet
  1971.       &Lbl2Stk#[&StkPtr#]: SETC ''    ; And no next Case# label either
  1972.       IF &t1 ≠ 'N' THEN
  1973.         AERROR 'Invalid JmpTbl specification -- JmpTbl=N assumed'
  1974.       ENDIF
  1975.     ENDIF
  1976. .*
  1977. .Exit    PRINT    Pop
  1978.         ENDM
  1979.  
  1980.  
  1981.         TITLE    'Case# - Switch# case'
  1982.         
  1983.         MACRO
  1984.         Case#.&Ext
  1985. .*
  1986. .******************************************************************************
  1987. .* Case# - Switch# case                                                       *
  1988. .*                                                                            *
  1989. .* Call:   Switch#.<sz> <selector>,Dreg=Dn,JmpTbl={<ext>|Y|N},ChkRng={Y|N}    *
  1990. .*         Case#.<ext> <ae1>[..<ae2>],...                                 *** *
  1991. .*         Default#                                                           *
  1992. .*         EndS#                                                              *
  1993. .*                                                                            *
  1994. .* Input:  &StkPtr#            = statment nesting stack pointer        (SETA) *
  1995. .*         &KindStk#[&StkPtr#] = statement nesting kind = 5            (SETA) *
  1996. .*         &Lbl1Stk#[&StkPtr#] = most recent Case# <ae> value          (SETC) *
  1997. .*         &Lbl2Stk#[&StkPtr#] = next Case# label suffix               (SETC) *
  1998. .*         &CaseStkPtr#        = case stack pointer for nested switches(SETA) *
  1999. .*         &Low#[&StkPtr#]     = lowest case value                     (SETA) *
  2000. .*         &High#[&StkPtr#]    = highest case value                    (SETA) *
  2001. .*         &JmpTbl#[&StkPtr#]  = 0==>SUB's; 1==>JumpTbl; 2==>ChkRng=Y  (SETA) *
  2002. .*         &Dreg#[&StkPtr#]    = D-register used by this Switch#       (SETC) *
  2003. .*                                                                            *
  2004. .* Output: &CaseStk#[&CaseStkPtr#]= Case# label stack (see below)      (SETC) *
  2005. .*         &Lbl1Stk#[&StkPtr#] = most recent Case# <ae> value          (SETC) *
  2006. .*         &Lbl2Stk#[&StkPtr#] = next Case# label suffix               (SETC) *
  2007. .*         &CaseStkPtr#        = case stack pointer for nested switches(SETA) *
  2008. .*         &Low#[&StkPtr#]     = lowest case value                     (SETA) *
  2009. .*         &High#[&StkPtr#]    = highest case value                    (SETA) *
  2010. .*                                                                            *
  2011. .*         Note: &CaseStk#[&CaseStkPtr#] always is the most recent Case# case *
  2012. .*               label used only when a jump table is to be generated.  We    *
  2013. .*               need to save up the labels to generate the table when we     *
  2014. .*               reach the EndS#.  &FrstCase#[&StkPtr#] points to the 1st     *
  2015. .*               case label in the &CaseStk#, while &CaseStkPtr# points to    *
  2016. .*               current (last) case label.  In order to save generating      *
  2017. .*               additional arrays, we keep additional info in the &CaseStk#  *
  2018. .*               entry so that we know the <ae> corresponding to this case,   *
  2019. .*               and also whether the case is a case tag range.  The format   *
  2020. .*               for a &CaseStk#[&CaseStkPtr#] is as follows:                 *
  2021. .*                                                                            *
  2022. .*               &CaseStk#[&CaseStkPtr#]: xxxxNNNN<ae>                        *
  2023. .*                                        1   5   9...                        *
  2024. .*                                                                            *
  2025. .*               where,  xxxx  = the case label suffix for labels %C%xxxx     *
  2026. .*                       NNNN  = number of case tags represented by the label *
  2027. .*                       <ae1> = 1st case tag in the range of NNNN tag values *
  2028. .*                                                                            *
  2029. .* Code:   There are two cases depending on whether a jump table is being     *
  2030. .*         used or not:                                                       *
  2031. .*                                                                            *
  2032. .*         • JumpTbl = <ext> | Yes (<ext> = W)                                *
  2033. .*                                                                            *
  2034. .*           * The following is generated by Switch#                          *
  2035. .*                    MOVEQ      #0,<Dreg>           ; If <sz> is B           *
  2036. .*                    MOVE.<sz>  <Selector>,<Dreg>                            *
  2037. .*                    BRA.<ext>  %L%xxxx                                      *
  2038. .*           ---------------------------------------------------------------- *
  2039. .*           * The following is generated by Case# (HERE IN THIS MACRO)       *
  2040. .*           %C%xxxx                                                          *
  2041. .*           ---------------------------------------------------------------- *
  2042. .*           * The following is generated by Default#                         *
  2043. .*           %D%xxxx                                                          *
  2044. .*           ---------------------------------------------------------------- *
  2045. .*           * The following is generated by EndS#                            *
  2046. .*           %L%xxxx                                                          *
  2047. .*                  [ CMPI.W     #Low,<Dreg>  ]      ; These 4 lines gened if *
  2048. .*                  [ BLT        %D%xxxx      ]      ; ChkRng=Yes on Switch#  *
  2049. .*                  [ CMPI.W     #High,<Dreg> ]      ; Low/High are the lowest*
  2050. .*                  [ BGT        %D%xxxx      ]      ; and highest Case# tags *
  2051. .*                    ADD.W      <Dreg>,<Dreg>       ; Get jump table entry   *
  2052. .*                    MOVE.W     %T%xxxx-(2*Low)(<Dreg>.W),<Dreg>             *
  2053. .*                    JMP        %T%xxxx(<Dreg>.W)   ; Jump to proper Case#   *
  2054. .*           %T%xxxx                                 ; The jump table         *
  2055. .*                    DCB.W      &High-&Low+1,%D%xxxx-%T%xxxx; Default holes  *
  2056. .*                    ORG        %T%xxxx+2*(<ae>-Low) ; Case# <ae>            *
  2057. .*                    DC.W       %C%xxxx-%T%xxxx                              *
  2058. .*                    - - -                                                   *
  2059. .*                    ORG        %T%xxxx+2*(<ae1>-Low); Case# <ae1>..<ae2>    *
  2060. .*                    DCB.W      <n>,%C%xxxx-%T%xxxx  ; <n> = <ae2>-<ae1>+1   *
  2061. .*                    - - -                                                   *
  2062. .*                    ORG                            ; Reset PC to end of tbl *
  2063. .*           %E%xxxx                                 ; Used by Leave# and as  *
  2064. .*                                                     default if no Default# *
  2065. .*                                                                            *
  2066. .*         • JumpTbl = No                                                     *
  2067. .*                                                                            *
  2068. .*           * The following is generated by Switch#                          *
  2069. .*                    MOVEQ      #0,<Dreg>           ; If <sz> is B           *
  2070. .*                    MOVE.<sz>  <Selector>,<Dreg>                            *
  2071. .*           ---------------------------------------------------------------- *
  2072. .*           * The following is generated by Case# (HERE IN THIS MACRO)       *
  2073. .*           %C%xxxx                                 ; Not on 1st Case#       *
  2074. .*                    -------------------------------; Not last Case# <ae>    *
  2075. .*                    SUB        #<ae>-<ae'>,<Dreg>  ; Case# <ae>,...         *
  2076. .*                    BEQ.S      %T%xxxx                                      *
  2077. .*                    -------------------------------; Last Case# tag range   *
  2078. .*                    SUB        #<ae1>-<ae>,<Dreg>  ; Case# ...,<ae1>..<ae2> *
  2079. .*                    BLT.<ext>  %C%xxx1             ; Next %C%xxxx Case# lbl *
  2080. .*                    SUB        #<ae2>-<ae1>,<Dreg>                          *
  2081. .*                    BGT.<ext>  %C%xxx1                                      *
  2082. .*                    ===============================; Not last Case# tag rng *
  2083. .*                    SUB        #<ae1>-<ae'>,<Dreg> ; Case# <ae1>..<ae2>,... *
  2084. .*                    BLT.S      @x                                           *
  2085. .*                    SUB        #<ae2>-<ae1>,<Dreg>                          *
  2086. .*                    BLE.S      %T%xxxx                                      *
  2087. .*                    -------------------------------; Last Case# <ae>        *
  2088. .*           @x       SUB        #<ae>-<ae2>,<Dreg>  ; Case# ...,<ae>         *
  2089. .*                    BNE.<ext>  %C%xxx1                                      *
  2090. .*           %T%xxxx                                                          *
  2091. .*           ---------------------------------------------------------------- *
  2092. .*           * The following is generated by Default#                         *
  2093. .*           %D%xxxx                                                          *
  2094. .*           ---------------------------------------------------------------- *
  2095. .*           * The following is generated by EndS#                            *
  2096. .*           %C%xxx1 [EQU         %D%xxxx]           ; EQU if no Default#     *
  2097. .*           %E%xxxx                                 ; Used by Leave#         *
  2098. .******************************************************************************
  2099. .*
  2100.         PRINT    Push,NoMDir,NoMCall
  2101. .*
  2102.         GBLA        &StkPtr#            ; statment nesting stack pointer
  2103.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  2104.         GBLC        &Lbl1Stk#[25]        ; most recent Case# <ae> value
  2105.         GBLC        &Lbl2Stk#[25]        ; next Case# label suffix
  2106.         GBLA        &CaseStkPtr#        ; case stack pointer for nested switches
  2107.         GBLA        &Low#[25]            ; lowest case value
  2108.         GBLA        &High#[25]        ; highest case value
  2109.         GBLA        &JmpTbl#[25]        ; 0==>SUB's; 1==>JumpTbl; 2==>ChkRng=Y
  2110.         GBLC        &Dreg#[25]        ; D-register used by this Switch#
  2111.         GBLC        &CaseStk#[250]        ; Case# label stack
  2112. .*
  2113.         LCLC        &Case,&Low,&High,&Dreg,&Prev
  2114.         LCLA        &Case1,&Case2,&t,&N,&i,&j
  2115. .*
  2116. .* Validate that the Case# is nested in an Switch# statement
  2117. .*
  2118.     IF &StkPtr# = 0 THEN
  2119.       AERROR 'CASE# not nested in an SWITCH#'
  2120.       GOTO .Exit
  2121.     ENDIF
  2122.     IF &KindStk#[&StkPtr#] ≠ 5 THEN
  2123.       AERROR 'CASE# not nested in an SWITCH#'
  2124.       GOTO .Exit
  2125.     ENDIF
  2126. .*
  2127. .* We will be processing &Nbr(&SysLst) Case# parameters, each of which may be
  2128. .* a case tag range, and all processed differently depending on whether a jump
  2129. .* table is being generated or not.
  2130. .*
  2131.     &N: SETA &Nbr(&SysLst)            ; Get number of Case# tag (ranges)
  2132. .*
  2133. .* If a jump table is to be generated, we build a &CaseStk#[&CaseStkPtr#] entry
  2134. .* for each Case# parameter with the info described in the intro comments. Note,
  2135. .* although there is one &CaseStk#[&CaseStkPtr#] for each parameter, the label
  2136. .* suffix part is the same for all the entries.
  2137. .*
  2138. .* If a jump table is not being generated, we determine the case by repeated
  2139. .* subtractions on the case selector register.  To do this we generate a SUB
  2140. .* of the difference between the new case tag and the previous case tag.  Ranges
  2141. .* are similar.  The previous case tag to use is kept in &Lbl1Stk#[&StkPtr#],
  2142. .* while the next Case# label to jump to if the subtract "fails" to produce a
  2143. .* case match is kept in &Lbl2Stk#[&StkPtr#].
  2144. .*
  2145.     IF &JmpTbl#[&StkPtr#] THEN        ; Doing a jump table Case# ?
  2146. %C%&SysNdx
  2147.       WHILE &i < &N DO                ; Yes, loop through all Case# parameters
  2148.         &i: SETA &i+1                ; Count the parameter
  2149.         &Case: SETC &SysLst[&i]        ; Pick it up
  2150.         &j: SETA &Pos('..', &Case)    ; See if we have a case tag range
  2151.         IF &j THEN                    ; Is it ?
  2152.           &Case1: SETA &Eval(&Case[1:&j-1])                ; Yes
  2153.          &Case2: SETA &Eval(&Case[&j+2:&Len(&Case)-&j-1]) ; Get both <ae>'s
  2154.         ELSE                        ; If not a case tag range...
  2155.           &Case1: SETA &Eval(&Case)    ; ...both <ae>'s are the same
  2156.          &Case2: SETA &Case1
  2157.          &j:     SETA 256            ; Fake out to set <ae> in &CaseStk# below
  2158.         ENDIF
  2159.         IF &CaseStkPtr# = 250 THEN    ; Make sure we can save the label info
  2160.          AERROR 'Too many "JmpTbl" cases -- max of 250 allowed'
  2161.          GOTO .Exit
  2162.         ENDIF
  2163.         IF &Case1 > &Case2 THEN        ; Make sure <ae1> <= <ae2>
  2164.           AERROR &Concat('Invalid CASE# range: ', &Case)
  2165.          &t: SETA &Case1            ; If <ae1> > <ae2>...
  2166.          &Case1: SETA &Case2        ; Invert the range for the hell of it!
  2167.          &Case2: SETA &t
  2168.         ENDIF
  2169.         &t: SETA &Case2-&Case1+1        ; Get nbr of case tags in range (NNNN)
  2170.         &CaseStkPtr#: SETA &CaseStkPtr#+1; Stack the case info in &CaseStk#
  2171.         &CaseStk#[&CaseStkPtr#]: SETC &Concat(&SysNdx, &I2S(&t, -4), &Case[1:&j-1])
  2172.         &Low#[&StkPtr#]: SETA &Min(&Low#[&StkPtr#], &Case1)   ; Get min tag
  2173.         &High#[&StkPtr#]: SETA &Max(&High#[&StkPtr#], &Case2) ; Get max tag
  2174.       ENDW
  2175.     ELSE                            ; Not doing a jump table Case#
  2176.       &Dreg: SETC &Dreg#[&StkPtr#]    ; Prepare to gen subtractions
  2177.       &Prev: SETC &Lbl1Stk#[&StkPtr#]    ; &Prev will hold previous <ae>
  2178.       IF &Prev ≠ '' THEN            ; Gen destination label if not 1st time
  2179. %C%&Lbl2Stk#[&StkPtr#]
  2180.       ENDIF
  2181.       &Lbl2Stk#[&StkPtr#]: SETC &SysNdx; Set destination to next Case#
  2182.       WHILE &i < &N DO                ; Gen subtractions for each parameter
  2183.         &i: SETA &i+1                ; Count the parameter
  2184.         &Case: SETC &SysLst[&i]        ; Pick it up
  2185.         &j: SETA &Pos('..', &Case)    ; See if we have a case tag range
  2186.         IF &j THEN                    ; Case tag range: Case# <ae1>..<ae2>,...
  2187.           &Low:  SETC &Case[1:&j-1]    ; Yes, extract both <ae>'s as strings
  2188.          &High: SETC &Case[&j+2:&Len(&Case)-&j-1]
  2189.          &Case1: SETA &Eval(&Low)    ; Get values of <ea>'s to check ordering
  2190.          &Case2: SETA &Eval(&High)
  2191.          IF &Case1 > &Case2 THEN        ; Make sure <ae1> <= <ae2>
  2192.            AERROR &Concat('Invalid CASE# range: ', &Case)
  2193.            &Case: SETC &Low            ; If <ae1> > <ae2>...
  2194.            &Low:  SETC &High        ; Invert the range for the hell of it!
  2195.            &High: SETC &Case        ; Note, here we using the strings
  2196.          ENDIF
  2197.          IF &i < &N THEN            ; Case# <ae1>..<ae2>,...
  2198. .*
  2199.           SUB.W     #&Low&Prev,&Dreg
  2200.           BLT.S     @&i
  2201.           SUB.W     #&High-&Low,&Dreg
  2202.           BLE.S     %T%&SysNdx
  2203. @&i
  2204. .*
  2205.          ELSE                    ; Case# ...,<ae1>..<ae2>
  2206. .*
  2207.           SUB.W     #&Low&Prev,&Dreg
  2208.           BLT.&Ext  %C%&SysNdx
  2209.           SUB.W     #&High-&Low,&Dreg
  2210.           BGT.&Ext  %C%&SysNdx
  2211. .*
  2212.          ENDIF
  2213.           &Prev: SETC &Concat('-', &High)
  2214.         ELSE                        ; Not a case tage range: Case# <ae>,...
  2215.          IF &i < &N THEN            ; Case# <ae>,...
  2216. .*
  2217.           SUB.W     #&Case&Prev,&Dreg
  2218.           BEQ.S     %T%&SysNdx
  2219. .*
  2220.          ELSE                    ; Case# ...,<ae>
  2221. .*
  2222.           SUB.W     #&Case&Prev,&Dreg
  2223.           BNE.&Ext  %C%&SysNdx
  2224. .*
  2225.          ENDIF
  2226.           &Prev: SETC &Concat('-', &Case)
  2227.         ENDIF
  2228.       ENDW
  2229.       IF &N ≠ 1 THEN                ; Gen "true" label if nore than 1 tag
  2230. %T%&SysNdx
  2231.       ENDIF
  2232.       &Lbl1Stk#[&StkPtr#]: SETC &Prev    ; Update for next Case# call
  2233.     ENDIF
  2234. .*
  2235. .Exit    PRINT    Pop
  2236.         ENDM
  2237.  
  2238.  
  2239.         TITLE    'Default# - Define default Switch# case'
  2240.  
  2241.         MACRO
  2242.         Default#
  2243. .*
  2244. .******************************************************************************
  2245. .* Default# - Define default Switch# case                                     *
  2246. .*                                                                            *
  2247. .* Call:   Switch#.<sz> <selector>,Dreg=Dn,JmpTbl={<ext>|Y|N},ChkRng={Y|N}    *
  2248. .*         Case#.<ext> <ae1>[..<ae2>],...                                     *
  2249. .*         Default#                                                       *** *
  2250. .*         EndS#                                                              *
  2251. .*                                                                            *
  2252. .* Input:  &StkPtr#            = statment nesting stack pointer        (SETA) *
  2253. .*         &KindStk#[&StkPtr#] = statement nesting kind = 5            (SETA) *
  2254. .*                                                                            *
  2255. .* Output: &Default#[&StkPtr#] = default label from Default#           (SETC) *
  2256. .*                                                                            *
  2257. .* Code:   There are two cases depending on whether a jump table is being     *
  2258. .*         used or not:                                                       *
  2259. .*                                                                            *
  2260. .*         • JumpTbl = <ext> | Yes (<ext> = W)                                *
  2261. .*                                                                            *
  2262. .*           * The following is generated by Switch#                          *
  2263. .*                    MOVEQ      #0,<Dreg>           ; If <sz> is B           *
  2264. .*                    MOVE.<sz>  <Selector>,<Dreg>                            *
  2265. .*                    BRA.<ext>  %L%xxxx                                      *
  2266. .*           ---------------------------------------------------------------- *
  2267. .*           * The following is generated by Case#                            *
  2268. .*           %C%xxxx                                                          *
  2269. .*           ---------------------------------------------------------------- *
  2270. .*           * The following is generated by Default# (HERE IN THIS MACRO)    *
  2271. .*           %D%xxxx                                                          *
  2272. .*           ---------------------------------------------------------------- *
  2273. .*           * The following is generated by EndS#                            *
  2274. .*           %L%xxxx                                                          *
  2275. .*                  [ CMPI.W     #Low,<Dreg>  ]      ; These 4 lines gened if *
  2276. .*                  [ BLT        %D%xxxx      ]      ; ChkRng=Yes on Switch#  *
  2277. .*                  [ CMPI.W     #High,<Dreg> ]      ; Low/High are the lowest*
  2278. .*                  [ BGT        %D%xxxx      ]      ; and highest Case# tags *
  2279. .*                    ADD.W      <Dreg>,<Dreg>       ; Get jump table entry   *
  2280. .*                    MOVE.W     %T%xxxx-(2*Low)(<Dreg>.W),<Dreg>             *
  2281. .*                    JMP        %T%xxxx(<Dreg>.W)   ; Jump to proper Case#   *
  2282. .*           %T%xxxx                                 ; The jump table         *
  2283. .*                    DCB.W      &High-&Low+1,%D%xxxx-%T%xxxx; Default holes  *
  2284. .*                    ORG        %T%xxxx+2*(<ae>-Low) ; Case# <ae>            *
  2285. .*                    DC.W       %C%xxxx-%T%xxxx                              *
  2286. .*                    - - -                                                   *
  2287. .*                    ORG        %T%xxxx+2*(<ae1>-Low); Case# <ae1>..<ae2>    *
  2288. .*                    DCB.W      <n>,%C%xxxx-%T%xxxx  ; <n> = <ae2>-<ae1>+1   *
  2289. .*                    - - -                                                   *
  2290. .*                    ORG                            ; Reset PC to end of tbl *
  2291. .*           %E%xxxx                                 ; Used by Leave# and as  *
  2292. .*                                                     default if no Default# *
  2293. .*                                                                            *
  2294. .*         • JumpTbl = No                                                     *
  2295. .*                                                                            *
  2296. .*           * The following is generated by Switch#                          *
  2297. .*                    MOVEQ      #0,<Dreg>           ; If <sz> is B           *
  2298. .*                    MOVE.<sz>  <Selector>,<Dreg>                            *
  2299. .*           ---------------------------------------------------------------- *
  2300. .*           * The following is generated by Case#                            *
  2301. .*           %C%xxxx                                 ; Not on 1st Case#       *
  2302. .*                    -------------------------------; Not last Case# <ae>    *
  2303. .*                    SUB        #<ae>-<ae'>,<Dreg>  ; Case# <ae>,...         *
  2304. .*                    BEQ.S      %T%xxxx                                      *
  2305. .*                    -------------------------------; Last Case# tag range   *
  2306. .*                    SUB        #<ae1>-<ae>,<Dreg>  ; Case# ...,<ae1>..<ae2> *
  2307. .*                    BLT.<ext>  %C%xxx1             ; Next %C%xxxx Case# lbl *
  2308. .*                    SUB        #<ae2>-<ae1>,<Dreg>                          *
  2309. .*                    BGT.<ext>  %C%xxx1                                      *
  2310. .*                    ===============================; Not last Case# tag rng *
  2311. .*                    SUB        #<ae1>-<ae'>,<Dreg> ; Case# <ae1>..<ae2>,... *
  2312. .*                    BLT.S      @x                                           *
  2313. .*                    SUB        #<ae2>-<ae1>,<Dreg>                          *
  2314. .*                    BLE.S      %T%xxxx                                      *
  2315. .*                    -------------------------------; Last Case# <ae>        *
  2316. .*           @x       SUB        #<ae>-<ae2>,<Dreg>  ; Case# ...,<ae>         *
  2317. .*                    BNE.<ext>  %C%xxx1                                      *
  2318. .*           %T%xxxx                                                          *
  2319. .*           ---------------------------------------------------------------- *
  2320. .*           * The following is generated by Default# (HERE IN THIS MACRO)    *
  2321. .*           %D%xxxx                                                          *
  2322. .*           ---------------------------------------------------------------- *
  2323. .*           * The following is generated by EndS#                            *
  2324. .*           %C%xxx1 [EQU         %D%xxxx]           ; EQU if no Default#     *
  2325. .*           %E%xxxx                                 ; Used by Leave#         *
  2326. .******************************************************************************
  2327. .*
  2328.         PRINT    Push,NoMDir,NoMCall
  2329. .*
  2330.         GBLA        &StkPtr#            ; statment nesting stack pointer
  2331.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  2332.         GBLC        &Default#[25]        ; default label from Default#
  2333. .*
  2334. .* Validate that the Default# is nested in an Switch# statement
  2335. .*
  2336.     IF &StkPtr# = 0 THEN
  2337.       AERROR 'DEFAULT# not nested in an SWITCH#'
  2338.       GOTO .Exit
  2339.     ENDIF
  2340.     IF &KindStk#[&StkPtr#] ≠ 5 THEN
  2341.       AERROR 'DEFAULT# not nested in an SWITCH#'
  2342.       GOTO .Exit
  2343.     ENDIF
  2344.     IF &Default#[&StkPtr#] ≠ '' THEN
  2345.       AERROR 'DEFAULT# already specified for this SWITCH#'
  2346.       GOTO .Exit
  2347.     ENDIF
  2348. .*
  2349. .* Define default case label and remember it for use by EndS#
  2350. .*
  2351. %D%&SysNdx
  2352. .*
  2353.     &Default#[&StkPtr#]: SETC &Concat('%D%', &SysNdx)
  2354. .*
  2355. .Exit    PRINT    Pop
  2356.         ENDM
  2357.  
  2358.  
  2359.         TITLE    'EndS# - End of Switch# statement'
  2360.  
  2361.         MACRO
  2362.         EndS#
  2363. .*
  2364. .******************************************************************************
  2365. .* EndS# - End of Switch# statement                                           *
  2366. .*                                                                            *
  2367. .* Call:   Switch#.<sz> <selector>,Dreg=Dn,JmpTbl={<ext>|Y|N},ChkRng={Y|N}    *
  2368. .*         Case#.<ext> <ae1>[..<ae2>],...                                     *
  2369. .*         Default#                                                           *
  2370. .*         EndS#                                                          *** *
  2371. .*                                                                            *
  2372. .* Input:  &StkPtr#            = statment nesting stack pointer        (SETA) *
  2373. .*         &KindStk#[&StkPtr#] = statement nesting kind = 5            (SETA) *
  2374. .*         &Lbl2Stk#[&StkPtr#] = next Case# label suffix               (SETC) *
  2375. .*         &CaseStkPtr#        = case stack pointer for nested switches(SETA) *
  2376. .*         &FrstCase#[&StkPtr#]= index of 1st Case# label in &CaseStk# (SETA) *
  2377. .*         &Low#[&StkPtr#]     = lowest case value                     (SETA) *
  2378. .*         &High#[&StkPtr#]    = highest case value                    (SETA) *
  2379. .*         &JmpTbl#[&StkPtr#]  = 0==>SUB's; 1==>JumpTbl; 2==>ChkRng=Y  (SETA) *
  2380. .*         &EndSwLbl#[&StkPtr#]= end case label suffix for Leave#      (SETC) *
  2381. .*         &Dreg#[&StkPtr#]    = D-register used by this Switch#       (SETC) *
  2382. .*         &Default#[&StkPtr#] = default label from Default#           (SETC) *
  2383. .*         &CaseStk#[&CaseStkPtr#]= Case# label stack (see Case#)      (SETC) *
  2384. .*                                                                            *
  2385. .* Output: &StkPtr#            = statment nesting stack ptr decremented(SETA) *
  2386. .*                                                                            *
  2387. .* Code:   There are two cases depending on whether a jump table is being     *
  2388. .*         used or not:                                                       *
  2389. .*                                                                            *
  2390. .*         • JumpTbl = <ext> | Yes (<ext> = W)                                *
  2391. .*                                                                            *
  2392. .*           * The following is generated by Switch#                          *
  2393. .*                    MOVEQ      #0,<Dreg>           ; If <sz> is B           *
  2394. .*                    MOVE.<sz>  <Selector>,<Dreg>                            *
  2395. .*                    BRA.<ext>  %L%xxxx                                      *
  2396. .*           ---------------------------------------------------------------- *
  2397. .*           * The following is generated by Case#                            *
  2398. .*           %C%xxxx                                                          *
  2399. .*           ---------------------------------------------------------------- *
  2400. .*           * The following is generated by Default#                         *
  2401. .*           %D%xxxx                                                          *
  2402. .*           ---------------------------------------------------------------- *
  2403. .*           * The following is generated by EndS# (HERE IN THIS MACRO)       *
  2404. .*           %L%xxxx                                                          *
  2405. .*                  [ CMPI.W     #Low,<Dreg>  ]      ; These 4 lines gened if *
  2406. .*                  [ BLT        %D%xxxx      ]      ; ChkRng=Yes on Switch#  *
  2407. .*                  [ CMPI.W     #High,<Dreg> ]      ; Low/High are the lowest*
  2408. .*                  [ BGT        %D%xxxx      ]      ; and highest Case# tags *
  2409. .*                    ADD.W      <Dreg>,<Dreg>       ; Get jump table entry   *
  2410. .*                    MOVE.W     %T%xxxx-(2*Low)(<Dreg>.W),<Dreg>             *
  2411. .*                    JMP        %T%xxxx(<Dreg>.W)   ; Jump to proper Case#   *
  2412. .*           %T%xxxx                                 ; The jump table         *
  2413. .*                    DCB.W      &High-&Low+1,%D%xxxx-%T%xxxx; Default holes  *
  2414. .*                    ORG        %T%xxxx+2*(<ae>-Low) ; Case# <ae>            *
  2415. .*                    DC.W       %C%xxxx-%T%xxxx                              *
  2416. .*                    - - -                                                   *
  2417. .*                    ORG        %T%xxxx+2*(<ae1>-Low); Case# <ae1>..<ae2>    *
  2418. .*                    DCB.W      <n>,%C%xxxx-%T%xxxx  ; <n> = <ae2>-<ae1>+1   *
  2419. .*                    - - -                                                   *
  2420. .*                    ORG                            ; Reset PC to end of tbl *
  2421. .*           %E%xxxx                                 ; Used by Leave# and as  *
  2422. .*                                                     default if no Default# *
  2423. .*                                                                            *
  2424. .*         • JumpTbl = No                                                     *
  2425. .*                                                                            *
  2426. .*           * The following is generated by Switch#                          *
  2427. .*                    MOVEQ      #0,<Dreg>           ; If <sz> is B           *
  2428. .*                    MOVE.<sz>  <Selector>,<Dreg>                            *
  2429. .*           ---------------------------------------------------------------- *
  2430. .*           * The following is generated by Case#                            *
  2431. .*           %C%xxxx                                 ; Not on 1st Case#       *
  2432. .*                    -------------------------------; Not last Case# <ae>    *
  2433. .*                    SUB        #<ae>-<ae'>,<Dreg>  ; Case# <ae>,...         *
  2434. .*                    BEQ.S      %T%xxxx                                      *
  2435. .*                    -------------------------------; Last Case# tag range   *
  2436. .*                    SUB        #<ae1>-<ae>,<Dreg>  ; Case# ...,<ae1>..<ae2> *
  2437. .*                    BLT.<ext>  %C%xxx1             ; Next %C%xxxx Case# lbl *
  2438. .*                    SUB        #<ae2>-<ae1>,<Dreg>                          *
  2439. .*                    BGT.<ext>  %C%xxx1                                      *
  2440. .*                    ===============================; Not last Case# tag rng *
  2441. .*                    SUB        #<ae1>-<ae'>,<Dreg> ; Case# <ae1>..<ae2>,... *
  2442. .*                    BLT.S      @x                                           *
  2443. .*                    SUB        #<ae2>-<ae1>,<Dreg>                          *
  2444. .*                    BLE.S      %T%xxxx                                      *
  2445. .*                    -------------------------------; Last Case# <ae>        *
  2446. .*           @x       SUB        #<ae>-<ae2>,<Dreg>  ; Case# ...,<ae>         *
  2447. .*                    BNE.<ext>  %C%xxx1                                      *
  2448. .*           %T%xxxx                                                          *
  2449. .*           ---------------------------------------------------------------- *
  2450. .*           * The following is generated by Default#                         *
  2451. .*           %D%xxxx                                                          *
  2452. .*           ---------------------------------------------------------------- *
  2453. .*           * The following is generated by EndS# (HERE IN THIS MACRO)       *
  2454. .*           %C%xxx1 [EQU         %D%xxxx]           ; EQU if no Default#     *
  2455. .*           %E%xxxx                                 ; Used by Leave#         *
  2456. .******************************************************************************
  2457. .*
  2458.         PRINT    Push,NoMDir,NoMCall
  2459. .*
  2460.         GBLA        &StkPtr#            ; statment nesting stack pointer
  2461.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  2462.         GBLC        &Lbl2Stk#[25]        ; next Case# label suffix
  2463.         GBLA        &CaseStkPtr#        ; case stack pointer for nested switches
  2464.         GBLA        &FrstCase#[25]        ; index of 1st Case# label in &CaseStk#
  2465.         GBLA        &Low#[25]            ; lowest case value
  2466.         GBLA        &High#[25]        ; highest case value
  2467.         GBLA        &JmpTbl#[25]        ; 0==>SUB's; 1==>JumpTbl; 2==>ChkRng=Y
  2468.         GBLC        &EndSwLbl#[25]        ; end case label suffix for Leave#
  2469.         GBLC        &Dreg#[25]        ; D-register used by this Switch#
  2470.         GBLC        &Default#[25]        ; default label from Default#
  2471.         GBLC        &CaseStk#[250]        ; Case# label stack
  2472. .*
  2473.         LCLC        &Dreg,&Lbl,&Default,&CaseLbl,&CaseValue
  2474.         LCLA        &Low,&High,&LastCase,&Case,&N,&Case1
  2475. .*
  2476. .* Validate that the EndS# is nested in an Switch# statement
  2477. .*
  2478.     IF &StkPtr# = 0 THEN
  2479.       AERROR 'ENDS# not nested in an SWITCH#'
  2480.       GOTO .Exit
  2481.     ENDIF
  2482.     IF &KindStk#[&StkPtr#] ≠ 5 THEN
  2483.       AERROR 'ENDS# not nested in an SWITCH#'
  2484.       GOTO .Exit
  2485.     ENDIF
  2486. .*
  2487. .* The &EndSwLbl#[&StkPtr#] label suffix generated in Switch# is used by the
  2488. .* code generated here (and by Leave#) to define the end of case label, the
  2489. .* jump table case branch-around label from Switch#, and the jump table label
  2490. .* itself. &Default#[&StkPtr#] may still be <null> if no Default# case was
  2491. .* specified.
  2492. .*
  2493.     &Lbl:     SETC &EndSwLbl#[&StkPtr#]; Pick up main Switch# label suffix
  2494.     &Default: SETC &Default#[&StkPtr#]    ; Pick up the default case label
  2495. .*
  2496. .* At this point it is time to generate the "bottom" part of the Switch#
  2497. .* statement.  If we are not generating a jump table, life is easy here!  All
  2498. .* the work was done in Case# to determine which case to use.  All that remains
  2499. .* here is to define the last case's "fail" label.  This is equated to the
  2500. .* default label if a Default# was specified.
  2501. .*
  2502. .* If we are to generate a jump table, now is the time to do it.  All Switch#
  2503. .* did was generate a branch to the code we generate here.  Case# calls just
  2504. .* defined case labels, and we saved them up in the &CaseStk# stack, computing
  2505. .* the low and high of all the Case# tag values along the way.  The low and high
  2506. .* values allow us to do the range check if required, and the low is needed
  2507. .* anyhow to offset the jump table origin.  Remember that, as described in
  2508. .* Case#, &FrstCase#[&StkPtr#] points to the first case lable entry in &CaseStk#,
  2509. .* and &CaseStkPtr# points at the last &CaseStk# entry.  Each entry has the
  2510. .* following format: &CaseStk#[&CaseStkPtr#]: xxxxNNNN<ae>                        *
  2511. .* where,  xxxx  = the case label suffix for labels %C%xxxx,
  2512. .*         NNNN  = number of case tags represented by the label,
  2513. .*         <ae1> = 1st case tag in the range of NNNN tag values.
  2514. .*
  2515.     IF &JmpTbl#[&StkPtr#] THEN        ; Generating a jump table ?
  2516.       &Case: SETA &FrstCase#[&StkPtr#]    ; Yes, get index to the first label
  2517.       IF &CaseStkPtr# < &Case THEN    ; Make sure we had at least one Case#
  2518.         AERROR 'No CASE#''s'
  2519.       ELSE
  2520.         &Dreg: SETC &Dreg#[&StkPtr#]    ; Get the D-reg we will be using
  2521.         &Low: SETA &Low#[&StkPtr#]    ; Get the low value of all case tags
  2522.         &High: SETA &High#[&StkPtr#]    ; Get the high value too
  2523. %L%&Lbl
  2524.         IF &Default = '' THEN        ; Did we have a Default# ?
  2525.           &Default: SETC &Concat('%E%',&Lbl); No, define it as the Leave# label
  2526.         ENDIF
  2527.         IF &JmpTbl#[&StkPtr#] = 2 THEN    ; Perform range check...
  2528.           PRINT Push,NoWarn            ; Ignore branch warnings here
  2529. .*
  2530.           CMPI.W    #&Low,&Dreg
  2531.           BLT       &Default
  2532.           CMPI.W    #&High,&Dreg
  2533.           BGT       &Default
  2534. .*
  2535.          PRINT Pop
  2536.         ENDIF
  2537. .*                                ; Gen code to index into table
  2538.           ADD.W     &Dreg,&Dreg
  2539.           MOVE.W    %T%&Lbl-(2*&Low)(&Dreg..W),&Dreg
  2540.           JMP       %T%&Lbl(&Dreg..W)
  2541. %T%&Lbl   
  2542.           DCB.W     &High-&Low+1,&Default-%T%&Lbl
  2543. .*
  2544.         WHILE &Case <= &CaseStkPtr# DO    ; Gen the table!!!
  2545.           &CaseLbl:   SETC &CaseStk#[&Case]       ; Extract Case# label info
  2546.          &Case1:     SETA &S2I(&CaseLbl[5:4]) ; &Case1   = NNNN = nbr of tags
  2547.          &CaseValue: SETC &CaseLbl[9:255]       ; &CaseValue = <ae1>
  2548.          &CaseLbl:   SETC &CaseLbl[1:4]       ; &CaseLbl = Case# label suffix
  2549.          IF &Case1 = 1 THEN                   ; If one tag, gen a DC else DCB
  2550. .*
  2551.           ORG       %T%&Lbl+2*(&CaseValue-&Low)
  2552.           DC.W      %C%&CaseLbl-%T%&Lbl     ; &CaseValue
  2553. .*
  2554.          ELSEIF &SubStr(&Trim(&CaseValue), 1, 1) = '''' THEN
  2555. .*
  2556.           ORG       %T%&Lbl+2*(&CaseValue-&Low)
  2557.           DCB.W     &Case1,%C%&CaseLbl-%T%&Lbl  ; &CaseValue...'&Chr(&Eval(&CaseValue)+&Case1-1)'
  2558. .*
  2559.          ELSE
  2560. .*
  2561.           ORG       %T%&Lbl+2*(&CaseValue-&Low)
  2562.           DCB.W     &Case1,%C%&CaseLbl-%T%&Lbl  ; &CaseValue...&Chr(&Eval(&CaseValue)+&Case1-1)
  2563. .*
  2564.          ENDIF
  2565.          &Case: SETA &Case+1
  2566.         ENDW
  2567.         &CaseStkPtr#: SETA &FrstCase#[&StkPtr#]-1 ; Next Case# label
  2568. .*
  2569.           ORG
  2570. .*
  2571.       ENDIF
  2572.     ELSE                            ; Not generating a jump table
  2573.       IF &Lbl2Stk#[&StkPtr#] = '' THEN    ; Make sure there was at least one Case#
  2574.         AERROR 'No CASE#''s'
  2575.       ELSEIF &Default = '' THEN        ; Define final Case# destination label
  2576. %C%&Lbl2Stk#[&StkPtr#]
  2577.       ELSE                        ; Equat final Case# dst lbl to default
  2578. %C%&Lbl2Stk#[&StkPtr#] EQU &Default
  2579.       ENDIF
  2580.     ENDIF
  2581. .*
  2582. .* Define the Leave# label
  2583. .*
  2584. %E%&Lbl
  2585. .*
  2586. .* Pop the EndS# statement off the statement nesting stack
  2587. .*
  2588.     &StkPtr#: SETA &StkPtr#-1
  2589. .*
  2590. .Exit    PRINT    Pop
  2591.         ENDM
  2592.  
  2593.  
  2594.         TITLE    'Cycle# - Loop iterator statement'
  2595.  
  2596.         MACRO
  2597.         Cycle#.&Ext &Who
  2598. .*
  2599. .******************************************************************************
  2600. .* Cycle# - Loop iterator statement                                           *
  2601. .*                                                                            *
  2602. .* Call:   Cycle#.<ext> [<label>] [If[#] <expr>]                              *
  2603. .*                                                                            *
  2604. .* Input:  &StkPtr#  = statment nesting stack pointer                  (SETA) *
  2605. .*         &KindStk# = statement nesting kind stack                    (SETA) *
  2606. .*         &LblStk#  = Repeat#, While#, For#, Switch# label stack      (SETC) *
  2607. .*         &Lbl2Stk# = label suffix stack for Cycle# in REPEAT#, FOR#  (SETC) *
  2608. .*                                                                            *
  2609. .* Output: &Lbl2Stk# = label suffix stack for Cycle# in REPEAT#,FOR#   (SETC) *
  2610. .******************************************************************************
  2611. .*
  2612.         PRINT    Push,NoMDir,NoMCall
  2613. .*
  2614.         GBLA        &StkPtr#            ; statment nesting stack pointer
  2615.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  2616.         GBLC        &LblStk#[25]        ; Repeat#, While#, or For# label stack
  2617.         GBLC        &Lbl2Stk#[25]        ; lbl suffix stack for Cycle# in REPEAT#,FOR#
  2618.         GBLA        &Cp#                ; scan pointer
  2619.         GBLA        &FalseUsed#        ; <ea> AND(OR) <ea> with &JumpCond=1(0)
  2620. .*
  2621.         LCLA        &Kind,&StkPtr
  2622.         LCLC        &Lbl,&Cond,&S
  2623. .*
  2624. .* Validate that the Cycle# is nested in a statement
  2625. .*
  2626.     IF &StkPtr# = 0 THEN
  2627.       AERROR 'CYCLE# must be nested inside a REPEAT#, WHILE#, or FOR#'
  2628.       GOTO .Exit
  2629.     ENDIF
  2630. .*
  2631. .* Get the label
  2632. .*
  2633.     IF &Who ≠ '' THEN                ; If any thing to scan...
  2634.       &Cp#: SETA &Lex(&Who, 1)        ; Scan 1st token
  2635.       IF &SysToken ≠ 0 THEN            ; We must have an idtentifier ?
  2636.         AERROR 'CYCLE# label or IF# expected'
  2637.         GOTO .Exit
  2638.       ENDIF
  2639.       &S: SETC &UC(&SysTokStr)        ; See if we have explicit label
  2640.       IF (&S ≠'IF') AND (&S ≠ 'IF#') THEN; If explicit label...
  2641.         &Lbl: SETC &SysTokStr        ; Pick it up
  2642.         &Cp#: SETA &Lex(&Who, &Cp#)    ; Scan for conditional (if any)
  2643.         IF &Systoken ≠ 30 THEN        ; If not end of line, we have a conditional
  2644.           &S: SETC &UC(&SysTokStr)    ; We now must have an "IF" or "IF#"
  2645.           IF (&S ≠ 'IF') AND (&S ≠ 'IF#') THEN; Is it ?
  2646.            AERROR 'Invalid CYCLE# conditional'
  2647.            GOTO .Exit
  2648.           ENDIF
  2649.           &Cond: SETC &Trim(&Who[&Cp#:255]); Extract conditional <expr>
  2650.         ENDIF
  2651.       ELSE                        ; If conditional but no explicit label
  2652.         &Cond: SETC &Trim(&Who[&Cp#:255]); Just extract conditional <expr>
  2653.       ENDIF
  2654.     ENDIF
  2655. .*
  2656. .* Scan the statement nesting stack from the inside out looking for the "proper"
  2657. .* Repeat#, While#, or For# loop continuation label to branch to. If an explicit
  2658. .* label is specified, it must be one on a Repeat#, While#, or For#.  If no
  2659. .* label is specified we look for the deepest Repeat#, While#, or For#, ignoring
  2660. .* any Switch#'s at the stack.
  2661. .*
  2662.     &StkPtr: SETA &StkPtr#            ; Prepare to scan the statement stack
  2663.     IF &Lbl ≠ '' THEN                ; Have explicit label to look for ?
  2664.       WHILE &LblStk#[&StkPtr]≠&Lbl DO    ; Look for it
  2665.         &StkPtr: SETA &StkPtr-1
  2666.         IF &StkPtr = 0 THEN
  2667.               AERROR &Concat(&Lbl, ' is not a REPEAT#, WHILE#, or FOR# label')
  2668.           GOTO .Exit
  2669.         ENDIF
  2670.       ENDW
  2671.     ELSE                            ; If no explicit label
  2672.       WHILE &KindStk#[&StkPtr] = 5 DO    ; Look for deepest loop after Switch#'s
  2673.         &StkPtr: SETA &StkPtr-1
  2674.         IF &StkPtr = 0 THEN
  2675.               AERROR &Concat(&Lbl, ' is not a REPEAT#, WHILE#, or FOR# label')
  2676.           GOTO .Exit
  2677.         ENDIF
  2678.       ENDW
  2679.     ENDIF
  2680. .*
  2681. .* At this point &StkPtr points to the the statement on the stack we are trying
  2682. .* to cycle.  If we have a Repeat# or For#, then the cycle label is defined in
  2683. .* &Lbl2Stk#[&StkPtr].  These two statements don't need to generate a cycle
  2684. .* label unless a Cycle# explicitly occurs.  So &Lbl2Stk#[&StkPtr] could be 
  2685. .* <null> here, in which, case we define the label.  Until# and For# check to see
  2686. .* if the label must be defined and do it in the appropriate place.  If we are
  2687. .* doing a Cycle# for a While# statement, that statement always needs a top of
  2688. .* the loop label.  It is saved in &LblStk#[&StkPtr], and we just use it here.
  2689. .* Note, that if we have a conditional Cycle#, then &Cond holds the <expr>,
  2690. .* otherwise it is <null>.
  2691. .* 
  2692.     &Kind: SETA &KindStk#[&StkPtr]    ; See which stmt we are doing Cycle# for
  2693.     IF (&Kind = 2) OR (&Kind = 4) THEN ; REPEAT#, FOR#
  2694.       IF &Lbl2Stk#[&StkPtr] = '' THEN
  2695.         &Lbl2Stk#[&StkPtr]: SETC &SysNdx
  2696.       ENDIF
  2697.       IF &Cond = '' THEN
  2698.           BRA.&Ext  %C%&Lbl2Stk#[&StkPtr]
  2699.       ELSE
  2700.         Expr#.&Ext &Cond,True=%C%&Lbl2Stk#[&StkPtr],False=%L%&SysNdx,JumpCond=1
  2701.         IF NOT &FalseUsed# GOTO .Exit    ; If we need False label, generate it
  2702. %L%&SysNdx
  2703.       ENDIF
  2704.     ELSEIF &Kind = 3 THEN            ; WHILE#
  2705.       IF &Cond = '' THEN
  2706.           BRA.&Ext  &LblStk#[&StkPtr]
  2707.       ELSE
  2708.         Expr#.&Ext &Cond,True=&LblStk#[&StkPtr],False=%L%&SysNdx,JumpCond=1
  2709.         IF NOT &FalseUsed# GOTO .Exit    ; If we need False label, generate it
  2710. %L%&SysNdx
  2711.       ENDIF
  2712.     ELSE
  2713.       AERROR 'CYCLE# must be nested inside a REPEAT#, WHILE#, or FOR#'
  2714.     ENDIF
  2715. .*
  2716. .Exit    PRINT    Pop
  2717.         ENDM
  2718.  
  2719.  
  2720.         TITLE    'Leave# - Loop and switch terminator'
  2721.  
  2722.         MACRO
  2723.         Leave#.&Ext &Who
  2724. .*
  2725. .******************************************************************************
  2726. .* Leave# - Loop and switch terminator                                        *
  2727. .*                                                                            *
  2728. .* Call:   Leave#.<ext> [<label>] [If[#] <expr>]                              *
  2729. .*                                                                            *
  2730. .* Input:  &StkPtr#  = statment nesting stack pointer                  (SETA) *
  2731. .*         &KindStk# = statement nesting kind stack                    (SETA) *
  2732. .*         &LblStk#  = Repeat#, While#, For#, Switch# label stack      (SETC) *
  2733. .*         &Lbl1Stk# = label suffix stack for Leave# in REPEAT#        (SETC) *
  2734. .*         &EndSwLbl#= end case label suffix stack for Leave#          (SETC) *
  2735. .*                                                                            *
  2736. .* Output: &Lbl1Stk# = label suffix stack for Leave# in REPEAT#        (SETC) *
  2737. .******************************************************************************
  2738. .*
  2739.         PRINT    Push,NoMDir,NoMCall
  2740. .*
  2741.         GBLA        &StkPtr#            ; statment nesting stack pointer
  2742.         GBLA        &KindStk#[25]        ; statement nesting kind stack
  2743.         GBLC        &LblStk#[25]        ; Repeat#,While#,For#,Switch# label stack
  2744.         GBLC        &Lbl1Stk#[25]        ; lbl suffix for Cycle# in REPEAT#,FOR#
  2745.         GBLC        &EndSwLbl#[25]        ; end case label suffix stack for Leave#
  2746.         GBLA        &Cp#                ; scan pointer
  2747.         GBLA        &FalseUsed#        ; <ea> AND(OR) <ea> with &JumpCond=1(0)
  2748. .*
  2749.         LCLA        &Kind,&StkPtr
  2750.         LCLC        &Lbl,&Cond,&S
  2751. .*
  2752. .* Validate that the Leave# is nested in a statement
  2753. .*
  2754.     IF &StkPtr# = 0 THEN
  2755.       AERROR 'LEAVE# must be nested inside a REPEAT#, WHILE#, or FOR#'
  2756.       GOTO .Exit
  2757.     ENDIF
  2758. .*
  2759. .* Get the label
  2760. .*
  2761.     IF &Who ≠ '' THEN                ; If any thing to scan...
  2762.       &Cp#: SETA &Lex(&Who, 1)        ; Scan 1st token
  2763.       IF &SysToken ≠ 0 THEN            ; We must have an idtentifier ?
  2764.         AERROR 'LEAVE# label or IF# expected'
  2765.         GOTO .Exit
  2766.       ENDIF
  2767.       &S: SETC &UC(&SysTokStr)        ; See if we have explicit label
  2768.       IF (&S ≠'IF') AND (&S ≠ 'IF#') THEN; If explicit label...
  2769.         &Lbl: SETC &SysTokStr        ; Pick it up
  2770.         &Cp#: SETA &Lex(&Who, &Cp#)    ; Scan for conditional (if any)
  2771.         IF &Systoken ≠ 30 THEN        ; If not end of line, we have a conditional
  2772.           &S: SETC &UC(&SysTokStr)    ; We now must have an "IF" or "IF#"
  2773.           IF (&S ≠ 'IF') AND (&S ≠ 'IF#') THEN; Is it ?
  2774.            AERROR 'Invalid LEAVE# conditional'
  2775.            GOTO .Exit
  2776.           ENDIF
  2777.           &Cond: SETC &Trim(&Who[&Cp#:255]); Extract conditional <expr>
  2778.         ENDIF
  2779.       ELSE                        ; If conditional but no explicit label
  2780.         &Cond: SETC &Trim(&Who[&Cp#:255]); Just extract conditional <expr>
  2781.       ENDIF
  2782.     ENDIF
  2783. .*
  2784. .* Scan the statement nesting stack from the inside out looking for the "proper"
  2785. .* Switch# or Repeat#, While#, or For# loop continuation label to branch to. If
  2786. .* an explicit label is specified, it must be one on a Switch#, Repeat#, While#,
  2787. .* or For#.  If no label is specified we use the deepest statement on the stack.
  2788. .*
  2789.     &StkPtr: SETA &StkPtr#            ; Prepare to scan the statement stack
  2790.     IF &Lbl ≠ '' THEN                ; Have explicit label to look for ?
  2791.       WHILE &LblStk#[&StkPtr]≠&Lbl DO    ; Look for it
  2792.         &StkPtr: SETA &StkPtr-1
  2793.         IF &StkPtr = 0 THEN
  2794.               AERROR &Concat(&Lbl, ' is not a REPEAT#, WHILE#, FOR#, or SWITCH# label')
  2795.           GOTO .Exit
  2796.         ENDIF
  2797.       ENDW
  2798.     ENDIF
  2799. .*
  2800. .* At this point &StkPtr points to the the statement on the stack we are trying
  2801. .* to leave. For a Repeat# statement, &Lbl1Stk#[&StkPtr] has the label suffix to
  2802. .* use. For Repeat#, While#, and For#, we use &Lbl1Stk#.  However, for Repeat#,
  2803. .* we generate the label here since Repeat# statements don''t need a Leave# label
  2804. .* unless a Leave# occurs.  Finally, for Switch#, we use the EndSwLbl# stack.
  2805. .* Note, that if we have a conditional Leave#, then &Cond holds the <expr>,
  2806. .* otherwise it is <null>.
  2807. .* 
  2808.     &Kind: SETA &KindStk#[&StkPtr]    ; See which stmt we are doing Leave# for
  2809.     IF &Kind = 2 THEN                ; REPEAT#
  2810.       IF &Lbl1Stk#[&StkPtr] = '' THEN
  2811.         &Lbl1Stk#[&StkPtr]: SETC &SysNdx
  2812.       ENDIF
  2813.       IF &Cond = '' THEN
  2814.           BRA.&Ext  %E%&Lbl1Stk#[&StkPtr]
  2815.       ELSE
  2816.         Expr#.&Ext &Cond,True=%E%&Lbl1Stk#[&StkPtr],False=%L%&SysNdx,JumpCond=1
  2817.         IF NOT &FalseUsed# GOTO .Exit    ; If we need False label, generate it
  2818. %L%&SysNdx
  2819.       ENDIF
  2820.     ELSEIF (&Kind=3) OR (&Kind=4) THEN ; WHILE#, FOR#
  2821.       IF &Cond = '' THEN
  2822.           BRA.&Ext  %E%&Lbl1Stk#[&StkPtr]
  2823.       ELSE
  2824.         Expr#.&Ext &Cond,True=%E%&Lbl1Stk#[&StkPtr],False=%L%&SysNdx,JumpCond=1
  2825.         IF NOT &FalseUsed# GOTO .Exit    ; If we need False label, generate it
  2826. %L%&SysNdx
  2827.       ENDIF
  2828.     ELSEIF &Kind = 5 THEN            ; SWITCH#
  2829.       IF &Cond = '' THEN
  2830.           BRA.&Ext  %E%&EndSwLbl#[&StkPtr]
  2831.       ELSE
  2832.         Expr#.&Ext &Cond,True=%E%&EndSwLbl#[&StkPtr],False=%L%&SysNdx,JumpCond=1
  2833.         IF NOT &FalseUsed# GOTO .Exit    ; If we need False label, generate it
  2834. %L%&SysNdx
  2835.       ENDIF
  2836.     ELSE
  2837.       AERROR 'LEAVE# must be nested inside a REPEAT#, WHILE#, FOR#, or SWITCH#'
  2838.     ENDIF
  2839. .*
  2840. .Exit    PRINT    Pop
  2841.         ENDM
  2842.  
  2843.  
  2844.         TITLE    'Dump file "FlowCtlMacs.d"'
  2845.         
  2846. ********************************************************************************
  2847.         DUMP        'FlowCtlMacs.d'
  2848. ********************************************************************************
  2849.         END